Generic-Funktion nur bei bestimmten Typen erlauben

CSHW89

Bekanntes Mitglied
Hi Leute,

ich hab gerade mal ein kleines nicht altägliches Generic-Problem. Vielleicht hat ja jemand eine Idee. Ich möchte eine Funktion in einer Generic-Klasse nur Objekten zur Verfügung stellen, die eine bestimmte Eigenschaft haben. Hier soll die addInt-Funktion nur aufrufbar sein, wenn der Parametertyp T von List<Integer> erbt.
Java:
import java.util.*;

public class TestGeneric<T> {
    
    private Map<String, T> map;
    
    public void addInt(String name, Integer value) {
        T arr = map.get(name);
        
        if (arr == null) {
            ArrayList<Integer> arr2 = new ArrayList<>();
            arr2.add(value);
            map.put(name, (T) arr2);
        }
        else {
            ((ArrayList<Integer>) z).add(value);
        }
    }
    
    public <F extends T & List<Integer>> void addInt2(String name, Integer value) {
        F arr = map.get(name);
        
        if (arr == null) {
            arr = new ArrayList<>();
            arr.add(value);
            map.put(name, arr);
        }
        else {
            z.add(value);
        }
    }
    
    public <F extends List<Integer> & T> void addInt3(String name, Integer value) {
        F arr = map.get(name);
        
        if (arr == null) {
            arr = new ArrayList<>();
            arr.add(value);
            map.put(name, arr);
        }
        else {
            z.add(value);
        }
    }
    
    public static void main() {
        new TestGeneric<List<Integer>>().addInt("h", 2);
        new TestGeneric<Object>().addInt("h", 2);  // soll Compiler-Error erzeugen
    }
    
}
Die Funktion addInt macht dies, allerdings nicht typensicher. In der main würde es dann zu einem Laufzeitfehler führen. Bei den anderen beiden Varianten hab ichs mit nem neuen Typparameter versucht. Beide lassen sich aber leider nicht compilieren.

Das hier ist übrigens nur ein Beispiel. Mein konkreter Fall sieht etwas anders aus. Es ist jetzt auch nicht mega wichtig, da die erste Variante ja funktioniert. Ich fände es nur interessant, ob es dafür ne Lösung gibt.

lg Kevin
 
Zuletzt bearbeitet:
Ich würde spontan sagen, dass das nicht geht. Zumindest nicht ohne eigene Compiler-Warnung zu schreiben. Vielleicht habe ich die Frage falsch verstanden, da du sehr viel unnötigen Quellcode gepostet hast ...

Java:
import java.util.List;

public class TestGeneric<T> {

	public void addInt(String name, Integer value) {}

	public static void main() {
		new TestGeneric<List<Integer>>().addInt("h", 2);
		new TestGeneric<Object>().addInt("h", 2); // soll Compiler-Error erzeugen
	}
}

Mit Vererbung:

Java:
public class TestGeneric<T> {}

public class TestGenericInt<T extends List<Integer>> extends TestGeneric<T> {

	public void addInt(String name, Integer value) {}

	public static void main() {
		new TestGenericInt<List<Integer>>().addInt("h", 2);
		new TestGenericInt<Object>().addInt("h", 2); // soll Compiler-Error erzeugen
		new TestGeneric<List<Integer>>().addInt("h", 2); // soll Compiler-Error erzeugen
	}
}
 
Naja du könntest das hier verwenden um den Objekttypen herauszufiltern
Java:
public void addInt(String name, Integer value) {
        if( T instanceof List<Integer> ) {
                T arr = map.get(name);
               
                if (arr == null) {
                    ArrayList<Integer> arr2 = new ArrayList<>();
                    arr2.add(value);
                    map.put(name, (T) arr2);
                }
                else {
                    ((ArrayList<Integer>) z).add(value);
                }
        }
}
 
Naja du könntest das hier verwenden um den Objekttypen herauszufiltern
Java:
if( T instanceof List<Integer>)
So ein Konstrukt funktioniert in Java NICHT! Die Generics sind nur zur Compile-Zeit bekannt. Der Kompilier ersetzt alle Generics durch entsprechende Casts, so dass du im Bytecode keine Generics mehr finden wirst.

Dementsprechend kann man diese auch nicht per instanceof prüfen. Bitte schreibe doch das nächste mal dazu, dass dies eine Vermutung von dir ist. Den Code so hinzuschreiben und damit zu propagieren, dass er funktionieren würde, halte ich für gefährlich.

EDIT: Zumal du den generischen Typparameter T sowieso nicht prüfen kannst (dieser ist noch nicht einmal eine Variable). :autsch: Wenn du dich mit Generics nicht auskennst (was dein Post nahelegt), dann halte dich bei solchen Themen doch bitte einfach zurück oder teste deinen Code, bevor du ihn postest. :rtfm:
 
Zuletzt bearbeitet:
@Ruzmanz: Ja stimmt, Vererbung wäre noch ne Möglichkeit. Allerdings wäre es in meinem Fall wieder etwas zu viel Overhead (aber danke für die Idee). Ich denke, ich bleibe bei meiner ersten typunsicheren Variante. Ich benutze es ja z.Z. nur für mich. Mich hatte es halt interessiert, ob es in Java möglich ist, einzelne Funktionen aus der Generic-Klasse sozusagen herauszufiltern bei bestimmten Generictypen.

Ich frage mich aber schon, warum der Multibound bei Funktionen nicht funktioniert:
Code:
<F extends T & List<Integer>>

@Natac: 🙂 ja wie du schon sagst, das kann aus zwei Gründen nicht funktionieren. T ist kein Objekt, und instanceof funktioniert nicht mit Generics.

lg Kevin
 
So ein Konstrukt funktioniert in Java NICHT! Die Generics sind nur zur Compile-Zeit bekannt. Der Kompilier ersetzt alle Generics durch entsprechende Casts, so dass du im Bytecode keine Generics mehr finden wirst.

Dann hast du denn Sinn von Generics nicht verstanden. Bei Generics ist es so, dass das Objekt dem Compiler NICHT bekannt ist, sondern erst zur Laufzeit!
Das ist der gesammte Sinn dahinter.
Und natürlich findet man keine Generics mehr später im Bytecode, es handelt sich hierbei ja auch nicht um Klassen/Objekte sondern um "Regeln" um den Compiler zu sagen, was man denn dort erwartet, damit er auf Fehler gegenprüfen kann.

Dementsprechend kann man diese auch nicht per instanceof prüfen. Bitte schreibe doch das nächste mal dazu, dass dies eine Vermutung von dir ist. Den Code so hinzuschreiben und damit zu propagieren, dass er funktionieren würde, halte ich für gefährlich.

Selbstverständlich kann man das per instanceof prüfen. Wenn dem Programm später nicht bekannt wäre um was für Objekte es sich dabei handelt, wie kann es dann die entsprechenden Methoden aufrufen?
Wie funktioniert dann das hier zur Laufzeit?:
(theoretischer Code) "[<T extends List<Integer>].add( 1 )"
Nach deiner Theorie dürfte die JRE zur Laufzeit garnicht wissen, dass es sich um ein Objekt das von List<Integer> erbt, handelt und könnte entsprechend nicht die Methode ".add(1)" aufrufen.

Zur Laufzeit sind die Informationen also da und man kann entsprechend auch Gegenprüfen per instanceof!
Ich finde es witzig, dass du mich dafür anmachst ich würde meinen Code nicht testen und das würde so nicht funktionieren, obwohl du ihn nicht einmal selbst gegengetestet hast!

Hier ein kleines KSKB das instanceof in Verbindung mit Generics zeigt
Java:
import java.util.ArrayList;

import javax.swing.JFrame;

public class Test<T> {
	public static void main(String[] args) {
		new Test<String>("String", "Hallo");
		new Test<Integer>("Integer", 1);
		new Test<JFrame>("JFrame", new JFrame());
		new Test<ArrayList<Integer>>("ArrayList<Integer>", new ArrayList<Integer>());
		new Test<ArrayList<String>>("ArrayList<String>", new ArrayList<String>());
	}
	
	public Test(String name, T test) {
		System.out.println("Object: " + name + " is " + ((test instanceof ArrayList<?>) ? "DEFINITLY" : "NOT") + " an instance of \"ArrayList<?>\"");
	}
}

Direkt auf List<Integer> kann man aber nicht testen, das war mir nicht bewusst gewesen. In C# funktionieren Generics in diesem Zusammenhang anderst, dort werden die Informationen direkt hinterlegt.
Das macht meinen Vorschlag in der Theorie aber nicht weniger Wert, in diesem Zusammenhang funktioniert er aber nicht, da List selbst noch auf Integer geprüft werden müsste.

EDIT: Zumal du den generischen Typparameter T sowieso nicht prüfen kannst (dieser ist noch nicht einmal eine Variable). :autsch:

In der Tat habe ich mich dort vertan bzw zu schnell gedacht. Aber die Idee sollte rübergekommen sein, ob ich jetzt T oder t schreibe. Vielen Dank für die Verbesserung.

Wenn du dich mit Generics nicht auskennst (was dein Post nahelegt), dann halte dich bei solchen Themen doch bitte einfach zurück oder teste deinen Code, bevor du ihn postest. :rtfm:

Jetzt haste dir aber ganz schön selbst ins Bein geschossen, kann das sein? Das nenn ich Karma - ich möchte dich daher bitte in Zukunft nicht mehr in Fragen über Generics sehen!

Aber frech ist es trotzdem allemal, insbesondere da ich wahrscheinlich mehr Jahre Programmiererfahrung habe, als du Jahre auf dem Buckel hast.
Man kann nunmal nicht immer richtig liegen. Ich opfere hier zumindest meine kostbare Freizeit unentgeltlich und beantworte Fragen auf dem Weg zur Arbeit um 5e morgens und nach 12Std wieder auf dem Weg zurück auf meinem Handy. Da habe ich keinen Compiler, ich bin aber auch keine Hausuafgabenmaschine die fertigen Code ausspuckt, sondern liefere Vorschläge. Ob und wie diese Vorschläge weiter verarbeitet werden kann ich nicht beeinflussen. Jeder Mensch sollte Intelligenz an den Tag legen können und wer sich nicht die Mühe macht, hat es auch nicht verdient und sollte sich vielleicht mit etwas anderem beschäftigen.


@TO
Da lag ich wohl tatsächlich leicht daneben. In deinem Anwendungsfall funktioniert das in der Praxis tatsächlich nicht (aber nicht aus den genannten Gründen).
An deiner Stelle würde ich mir aber eher Gedanken machen über die Programmarchitektur. Wenn bei mir einer mit so einem Code ankommen würde, dann gäbs n Satz heiße Ohre. 😉 🙂
 
Zuletzt bearbeitet:
Okay... halten wir als erstes fest, das mein Post vielleicht etwas zu aggressiv verfasst wurde. Sorry dafür. Wollte in keiner Weise deine Programmiererfahrung oder den Einsatz deiner Freizeit in Frage stellen.

Ich hatte mich auf folgende von dir geposteten Codeschnipsel bezogen:
Java:
if( T instanceof List<Integer> ) {
  T arr = map.get(name);
Für mich siehst du hier T als Typ an (
Code:
T arr =
). Daher mein Hinweis, dass man den generischen Typ gar nicht prüfen kann. Das du eigentlich
Code:
t
(als Variable) meinst, ist aus dem Code leider nicht ersichtlich.

Und das man gar nicht auf
Code:
List<Integer>
prüfen kann, hast du ja schon selbst eingesehen.

Mehr wollte ich mit meinem Post gar nicht sagen. Die Kombination deines "Vertippens"(t/T) und das Detail, dass man eben nicht gegen List<Integer> prüfen kann, haben eben einen falschen Eindruck von dir bei mir hinterlassen. :bahnhof:
 
Zuletzt bearbeitet:

Zurück
Oben