Generics und java.beans.Introspector

8

Mit dem folgenden Code-Skelett kann festgestellt werden, dass die Eigenschaft foo tatsächlich vom Typ String ?

ist %Vor%

Die tatsächliche Ausgabe ist

  

foo der Klasse java.lang.Object
  TypeVariable: T-Klasse java.lang.Object

Edit: Ich hätte erwähnen sollen, dass ich über das Löschen von Typen Bescheid weiß und dass die Methode tatsächlich ein Objekt auf der Bytecodeebene zurückgibt. Die Metadaten zu generischen Typen stehen jedoch in der Klassendatei zur Verfügung und können wie im Beispielcode durch Reflektion abgefragt werden. Hier ist ein weiteres Snippet, das zeigt, dass SubBean tatsächlich einen Typparameter vom Typ String hat:

%Vor%

Ausgabe:

  

Klasse java.lang.String

Die Frage bleibt dann, wie verbinde ich dieses tatsächliche Typargument mit der Typvariablen? Wenn ich weiß, dass es nur einen Typparameter gibt, ist das einfach, aber ich möchte, dass dieser Code auch für Beans mit mehreren generischen Typparametern funktioniert.

    
Jörn Horstmann 02.02.2011, 15:34
quelle

7 Antworten

5

Solange die Laufzeitklasse des Objekts den Wert des Typparameters bestimmt, können Sie dessen tatsächlichen Wert ableiten, indem Sie die Parameter des formalen Typs rekursiv durch die tatsächlichen ersetzen, die Sie aus Class.getGenericSuperClass () erhalten haben:

%Vor%

Testcode:

%Vor%

Wenn andererseits die Klasse den Wert des Typparameters nicht bestimmt, müssen Sie Ihr Bean erweitern, um das Klassenobjekt für den eigentlichen Typparameter zur Laufzeit auf andere Weise, z. indem Sie:

%Vor%     
meriton 03.02.2011, 01:12
quelle
3

Ich habe eine Lösung für den Fall gefunden, dass es eine Hierarchie mit einem einzigen Super gibt Klasse (abgesehen von Object), die auch funktioniert, wenn es mehrere Typparameter für die Superklasse gibt.

Funktioniert immer noch nicht für tiefere Hierarchien oder bei der Implementierung von generischen Schnittstellen. Auch ich möchte eine Bestätigung, dass dies tatsächlich dokumentiert ist und sollte funktionieren.

%Vor%

...

%Vor%

Ausgabe:

  

bar der Klasse java.lang.Object
  Übereinstimmung: B: Klasse java.lang.Integer
  foo der Klasse java.lang.Object
  Übereinstimmung: F: Klasse java.lang.String
  Qux der Klasse java.lang.Object
  Übereinstimmung: Q: X

Ich muss noch einige Tests schreiben, um zu entscheiden, was ich mit dem letzten Fall tun soll:)

    
Jörn Horstmann 02.02.2011 21:26
quelle
2

Sie können den Laufzeittyp von generic mit Hilfe von dieser Hack . Code aus der Verknüpfung extrahiert.

%Vor%     
Gursel Koca 02.02.2011 15:50
quelle
1

Java-Generatoren haben während der Kompilierung eine Art der Löschung. Zur Laufzeit ist es nicht möglich, den Typ von T zu ermitteln, der beim Kompilieren vorhanden war.

Hier ist ein Link: Tipp löschen

    
DwB 02.02.2011 15:39
quelle
1

Leider ist Typ Löschung in Kraft.

Obwohl die SubBean einen festen String-Typ für diesen ivar und diese Methoden haben sollte, weil der type-Parameter für SuperBean zur Kompilierungszeit bekannt ist, funktioniert das leider nicht so. Der Compiler erstellt keine String -ige Version von SuperBean zur Kompilierzeit, von der SubBean abgeleitet werden kann - es gibt nur eine (vom Typ gelöschte) SuperBean

Eine möglicherweise hässliche Problemumgehung, die mir jedoch vorkommt, ist, dass SubBean möglicherweise die Superklassenmethode mit der typspezifischen Version überschreiben kann und BeanInfo dann das zurückgibt, was Sie für die Methoden erwarten:

%Vor%

Update: Das obige funktioniert nicht. Beachten Sie diese Information, die @ Jörn Horstmann in den Kommentaren posten:

  

Dies scheint nicht zu funktionieren, da der Introspector immer noch eine Lese-Methode vom Typ Object zurückgibt. Darüber hinaus scheint dies eine generierte Bridge-Methode zu sein ( Ссылка ), was bedeutet, dass ich vielleicht in < a href="http://bugs.sun.com/view_bug.do?bug_id=6788525"> bugs.sun.com/view_bug.do?bug_id=6788525 wenn ich auf Anmerkungen zu dieser Methode zugreifen möchte.

Eine weitere hässliche Variante der obigen Problemumgehung ist das Aliasing der Eigenschaft:

%Vor%

SubBean hat jetzt eine eindeutige Eigenschaft FooItem , die ein Alias ​​für die ursprüngliche SuperBean Eigenschaft Foo ist.

    
Bert F 02.02.2011 15:55
quelle
0

Leider nein:

  

Generics werden durch Typ-Löschen implementiert: generische Typ-Informationen sind nur zur Kompilierzeit vorhanden, nach denen sie vom Compiler gelöscht werden. Der Hauptvorteil dieses Ansatzes besteht darin, dass er eine vollständige Interoperabilität zwischen generischem Code und Legacy-Code bietet, der nicht parametrisierte Typen verwendet (die technisch als unformatierte Typen bekannt sind). Die Hauptnachteile bestehen darin, dass Parametertypinformationen zur Laufzeit nicht verfügbar sind und dass automatisch generierte Umwandlungen fehlschlagen können, wenn sie mit fehlerhaftem Legacy-Code zusammenarbeiten. Es gibt jedoch eine Möglichkeit, eine garantierte Laufzeittypsicherheit für generische Sammlungen selbst dann zu erreichen, wenn sie mit schlechtem altem Code interoperieren.

Wie aus Ссылка

zitiert     
Eric Giguere 02.02.2011 15:42
quelle
0

Hier ist der Bytecode von SuperBean :

%Vor%

Wie Sie sehen, sind sowohl der Getter als auch der Setter vom Typ java.lang.Object. Introspector verwendet Getters und Setter, um den PropertyDescriptor zu generieren (Felder werden ignoriert), sodass die Eigenschaft den generischen Typ von T nicht kennen kann.

    
Sean Patrick Floyd 02.02.2011 15:52
quelle