Verstaendnisfrage Generics

Hi,

ich spiele gerade mit Generics rum und frage mich was die beste Loesung ist. Folgender Code funktioniert nicht:

Java:
public class Generic<T> {
    
    public static void main(String[] args) {
        String str = "hello";
        Generic<? extends String> g = new Generic<>(str.getClass());
        g.doStuff(str);
    }

    private Class<T> type;

    public Generic(Class<T> type) {
        this.type = type;
    }

    private void doStuff(T arg) {
        System.out.println(arg);
    }
    
}
Dieser Code funktioniert, aber new Generic(str.getClass()); hat gar keine Typ-Information und die IDE beschwert sich auch. Zwar nur eine Warnung, aber ich frage mich ob es besser geht?

Java:
public class Generic<T> {
    
    public static void main(String[] args) {
        String str = "hello";
        Generic<String> g = new Generic(str.getClass());
        g.doStuff(str);
    }

    private Class<T> type;

    public Generic(Class<T> type) {
        this.type = type;
    }

    private void doStuff(T arg) {
        System.out.println(arg);
    }
    
}
 
Aktuell benutzt du das type Feld ja gar nicht. Deswegen:
Java:
public class Generic<T> {
    public static void main(String[] args) {
        String str = "hello";
        Generic<String> g = new Generic<>();
        g.doStuff(str);
    }
    private void doStuff(T arg) {
        System.out.println(arg);
    }
}
 
K

kneitzel

Wie wäre es mit so einer Lösung:
Code:
public class Generic<T> {
    
    public static void main(String[] args) {
        String str = "hello";
        Generic<String> g = new Generic<>(String.class);
        g.doStuff(str);
    }

    private Class<T> type;

    public Generic(Class<T> type) {
        this.type = type;
    }

    private void doStuff(T arg) {
        System.out.println(arg);
    }
}
 
Oder mit der class-aus-empty-varargs Methode:
Java:
public class Generic<T> {
    public static void main(String[] args) {
        String str = "hello";
        Generic<String> g = new Generic<>();
        g.doStuff(str);
    }

    private Class<T> type;

    @SuppressWarnings("unchecked")
    public Generic(T... __dummy) {
        this.type = (Class<T>) __dummy.getClass().getComponentType();
    }

    private void doStuff(T arg) {
        System.out.println(arg);
    }
}
 
Wie wäre es mit so einer Lösung:
Code:
public class Generic<T> {
   
    public static void main(String[] args) {
        String str = "hello";
        Generic<String> g = new Generic<>(String.class);
        g.doStuff(str);
    }

    private Class<T> type;

    public Generic(Class<T> type) {
        this.type = type;
    }

    private void doStuff(T arg) {
        System.out.println(arg);
    }
}
Ich wollte ungern die Klasse explizit angeben, da dies zu Fehlern fuehren kann. Bei meinem echten Beispiel handelt es sich um eine Serialisierungsklasse die ein Objekt speichern und auch wieder laden soll. Ich will direkt das Objekt nutzen koennen um festzulegen fuer welche Objekttypen diese Klasse "funktionieren" soll. Wenn ich die Klasse immer per Hand explizit eingtrage und nicht per object.getclass() hole, koennten Fehler passieren.

Wundert mich schon, warum das nicht geht.
 
Oder mit der class-aus-empty-varargs Methode:
Java:
public class Generic<T> {
    public static void main(String[] args) {
        String str = "hello";
        Generic<String> g = new Generic<>();
        g.doStuff(str);
    }

    private Class<T> type;

    @SuppressWarnings("unchecked")
    public Generic(T... __dummy) {
        this.type = (Class<T>) __dummy.getClass().getComponentType();
    }

    private void doStuff(T arg) {
        System.out.println(arg);
    }
}
Geht natuerlich. Interessanter Hack. Nur leider auch irgendwie sehr seltsam. Hat type ueberhaupt einen Wert wenn man den Konstruktor ohne Argument aufruft?
 
Ich wollte ungern die Klasse explizit angeben, da dies zu Fehlern fuehren kann. Bei meinem echten Beispiel handelt es sich um eine Serialisierungsklasse die ein Objekt speichern und auch wieder laden soll. Ich will direkt das Objekt nutzen koennen um festzulegen fuer welche Objekttypen diese Klasse "funktionieren" soll. Wenn ich die Klasse immer per Hand explizit eingtrage und nicht per object.getclass() hole, koennten Fehler passieren.

Wundert mich schon, warum das nicht geht.
in der Deklaration des Typs hast du die Klasse auch per Hand angegeben ;)

Ob das geht, hängt vom konkreten Fall ab, an so einem „einfachen“ Beispiel ist das schwierig zu beurteilen
 
in der Deklaration des Typs hast du die Klasse auch per Hand angegeben ;)

Ob das geht, hängt vom konkreten Fall ab, an so einem „einfachen“ Beispiel ist das schwierig zu beurteilen
Meinst du String str = "hello"; ?

War ja nur ein Beispiel. Normalerweise haette ich das Objekt schon und wuerde es nicht erstellen sondern nur nutzen.
 
K

kneitzel

Aber um das Objekt nutzen zu können wirst Du das doch in irgend einer Variable haben. Also musst Du da doch irgend etwas von dem Objekt wissen. Aber dann wäre es halt Object statt String. Und wenn Du es als "CoolSuperClass" hast, dann ist es halt CoolSuperClass.

Im Augenblick verstehe ich nicht ganz Dein Problem.
 
Wundert mich schon, warum das nicht geht.
getClass() liefert Class<? extends String> und nicht Class<String>. Dem entsprechend müsstest Du Generic<? extends String> deklarieren, was aber dazu führt, dass Dein doStuff nicht mehr funktioniert, da nicht klar ist, ob das Objekt dem ursprünglichen Capture entspricht.
 
Nimm statt String andere Typen, zB Number und Integer, vielleicht wird's damit klarer:

Java:
Number n = Integer.valueOf(42);
Generic<Number> g = new Generic<>(n.getClass());
EDIT: muss natürlich valueOf und nicht of heißen...
 
Zuletzt bearbeitet:
Nimm statt String andere Typen, zB Number und Integer, vielleicht wird's damit klarer:

Java:
Number n = Integer.valueOf(42);
Generic<Number> g = new Generic<>(n.getClass());
EDIT: muss natürlich valueOf und nicht of heißen...
Nicht wirklich :)
Klar ist Number und Integer nicht wirklich "kompatibel" in allen Bereichen. Da wuerde es mich nicht wundern, dass es ein Problem gibt.
Ich wunder mich halt warum Interger.class als Argument anderst ist als n.getClass(). Das Objekt sollte immer Integer.class zurueckliefern ob es jetzt in einer Number-Variable steckt oder nicht. Das Problem ist, dass n.getClass() halt nicht Class<Integer> zurueckliefert sondern Class<? extends Integer>. Warum eigentlich?
Mir ist nicht bewusst warum der Compiler hier nicht schlauer sein kann, vor allem wenn ich z.B. sowas schreibe:
Generic<Integer> g = new Generic<>(new Integer(42).getClass());
Das ist nun doch wirklich eindeutig.
 
Ich wunder mich halt warum Interger.class als Argument anderst ist als n.getClass(). Das Objekt sollte immer Integer.class zurueckliefern ob es jetzt in einer Number-Variable steckt oder nicht. Das Problem ist, dass n.getClass() halt nicht Class<Integer> zurueckliefert sondern Class<? extends Integer>. Warum eigentlich?
Generics werden zur Compilezeit ausgewertet - und zur Compilezeit ist nur bekannt, das n vom Number ist.
n.getClass() kann also nicht Class<Integer> ergeben, weil das zur Compilezeit noch nicht feststeht - zur Compilezeit steht nur fest, dass es ein Subtyp von Number (oder Number) ist, also Class<? extends Number>.



Mir ist nicht bewusst warum der Compiler hier nicht schlauer sein kann, vor allem wenn ich z.B. sowas schreibe:
Generic<Integer> g = new Generic<>(new Integer(42).getClass());
Das ist nun doch wirklich eindeutig.
getClass gibt halt immer Class<? extends X> zurück, da Sonderregelungen für ein paar Spezialfälle einzuführen dürfte nicht unbedingt zielführend sein.

Vor allem da man das ja gleichwertig ersetzen kann mit Generic<Integer> g = new Generic<>(Integer.class);
 
Weil getClass eben einen Subtyp des statischen Typs zurückgibt, und nicht den statischen Typ.

Beispiel steht doch oben :)
Generics werden zur Compilezeit ausgewertet - und zur Compilezeit ist nur bekannt, das n vom Number ist.
n.getClass() kann also nicht Class<Integer> ergeben, weil das zur Compilezeit noch nicht feststeht - zur Compilezeit steht nur fest, dass es ein Subtyp von Number (oder Number) ist, also Class<? extends Number>.
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben