 |
AppletTalk.com Java discussions newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Michael Paap Guest
|
Posted: Wed May 16, 2007 5:39 pm Post subject: Type Erasure Tücken |
|
|
Liebe Leute,
gegeben sei folgender Code:
-----------------------------------------------------------------------
interface Interface1 {
void m1();
}
interface Interface2 {
void m2();
}
class X<T extends Interface1 & Interface2> {
private T a;
X(T a) {
this.a = a;
}
public T getA() {
return a;
}
}
class C implements Interface1 {
public void m1() {}
}
-----------------------------------------------------------------------
X ist hier mit einem Typ T parametrisiert, der zwei Schranken hat. Nach
dem, was ich bzgl. Type Erasure glaubte, verstanden zu haben, sollte im
Bytecode nur die erste Schranke auftauchen und genau das bestätigt mir
auch der Decompiler, der liefert:
-----------------------------------------------------------------------
class X {
X(Interface1 a) {
this.a = a;
}
public Interface1 getA() {
return a;
}
private Interface1 a;
}
-----------------------------------------------------------------------
So weit, so gut. Nehmen wir jetzt mal an, ich habe das da oben
übersetzt, und nun nur noch die Class-Dateien. Dann sollte doch in
X.class keine wie auch immer geartete Information über Interface2
vorliegen. Und dann sollte sich das hier übersetzen lassen:
-----------------------------------------------------------------------
class GenericTest {
X<C> x;
}
-----------------------------------------------------------------------
Lässt es sich aber nicht... der Compiler meldet mir:
"Bound mismatch: The type C is not a valid substitute for the bounded
parameter <T extends Interface1 & Interface2> of the type X<T>"
Woher weiß er das und wo wird das ggf. in der Generics-Faq
(http://angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html) erklärt?
Gruß,
Michael Paap |
|
| Back to top |
|
 |
Ralf Ullrich Guest
|
Posted: Wed May 16, 2007 5:39 pm Post subject: Re: Type Erasure Tücken |
|
|
Michael Paap wrote:
| Quote: | X ist hier mit einem Typ T parametrisiert, der zwei Schranken hat. Nach
dem, was ich bzgl. Type Erasure glaubte, verstanden zu haben, sollte im
Bytecode nur die erste Schranke auftauchen und genau das bestätigt mir
auch der Decompiler, der liefert:
|
Da hast a) etwas falschverstand und b) einen Generics-unaware Decompiler
verwendet.
Die zusätzlichen Constraints sind in der .class-Datei in sogenannten
Attributes enthalten mit denen zum Beispiel auch Debugging-Informationen
in die .class-Datei geschrieben werben; also zugebgeben nicht direkt im
Bytecode plus Methoden-Deskriptoren.
Diese Attribute, es geht bei den Generics um die Signature Attribute
(siehe 4.4.4 in diesem PDF
http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf
), kannst du aber mit Reflection auch selbst auslesen.
| Quote: | Woher weiß er das und wo wird das ggf. in der Generics-Faq
(http://angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html) erklärt?
|
Aus den Signature Attributen. Und in der FAQ wird das glaub ich nicht
erklärt, denn diese Details der Reification spielen nur eine Rolle bei der
Kompatibilität zwischen alten nicht generifizierten .class-Dateien und neu
übersetzten generifizierten .class-Dateien.
In deinem Beispiel also:
1. Kompiliere deinen nicht generifizierten Code mit einem Java 1.4 JDK
(oder crosscompile via -target 1.4 gegen eine 1.4er Runtime mit einem
aktuellen JDK).
Hier erstmal nochmal der nicht-generifizierte Code:
interface Interface1 {
void m1();
}
interface Interface2 {
void m2();
}
class X {
private Interface1 a;
X(Interface1 a) {
// eigener (optionaler) runtime check für zweiten Bound
if (!(a instanceof Interface2) {
throw new IllegalArgumentException();
}
this.a = a;
}
public Interface1 getA() {
return a;
}
}
class C implements Interface1 {
public void m1() {}
}
class GenericTest {
X x;
}
Das wird sich alles kompilieren lassen (wenn ich jetzt keine Tippfehler
gemacht habe.)
Wenn du danach hergehst und X generifizierst und nur dessen .class-File
neu übersetzst, dann sollte dein Programm mit dem alten
nicht-generifizierten GenericTest sauber funktionieren.
Aber eine Neu-Kompilierung von GenericTest mit einem Java 5 JDK oder
aktueller ist gezwungen die Signature-Attribue bei der Typ-Prüfung während
der Kompilierung zu berücksichtigen, folglich dein beobachteter Fehler.
Und zwar genau der Fehler, der zuvor nur zur Laufzeit überhaupt entdeckt
werden konnte, und nun wegen verletzter Typschranken im Source-Code, schon
zur Übersetzungszeit entdeckt werden kann.
Hoffe das macht es etwas klarer, wenn nicht weiterfragen.
cu |
|
| Back to top |
|
 |
Ralf Ullrich Guest
|
Posted: Wed May 16, 2007 5:39 pm Post subject: Re: Type Erasure Tücken |
|
|
Michael Paap wrote:
| Quote: | Da hast a) etwas falschverstand
Ich zitiere mal aus der Generics-FAQ
(file:///D:/HTTrack/Java%20Generics%20FAQ/www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ101):
"When the compiler finds the definition of a generic type or method, it
removes all occurrences of the type parameters and replaces them by
their leftmost bound, or type Object if no bound had been specified."
Wenn ich dich recht verstehe, ist diese Formulierung aber doch
mindestens arg irreführend... oder habe ich wirklich falsche Schlüsse
gezogen?
|
Naja, die Angelika-Langer-Generics-FAQ befasst sich, soweit ich das sehe,
überwiegend mit den Auswirkungen der Generics für den Programmierer. Bei
der tatsächlichen Umsetzung derselben, vor allem hinsichtlich des
Class-File-Formats scheint sie mir hingegen recht grob und ungenau zu
sein. Ich meine irgendwo muss eine FAQ ja schließlich auch die Grenze
ziehen, sonst ist es keine FAQ mehr, sondern nur noch eine umformulierte
(und dadurch meist fehlerhafte) Spec.
| Quote: | und b) einen Generics-unaware Decompiler
verwendet.
Ich habe Jad verwendet. Hast du einen Tip, was ich verwenden könnte, um
mehr zu sehen?
|
Nee. Ich muss gestehen, ich verwende so selten einen Decompiler und
bislang niemals auf generifizierten Code, dass ich dir da keine Empfehlung
geben kann. Die meisten Decompiler sind zwar offenbar inzwischen so
angepasst, dass sie das Java 5 Class-File-Format lesen können, ignorieren
aber das Signature Attribut, sind also nach wie vor reine Java 2
Decompiler. (Solange dein Decompiler nicht generifizierten Code ausspuckt,
ignoriert er es.) Würde mich aber auch interessieren, ob es inzwischen
echte Java 5 Decompiler gibt.
| Quote: | Und in der FAQ wird das glaub ich nicht
erklärt, denn diese Details der Reification spielen nur eine Rolle bei der
Kompatibilität zwischen alten nicht generifizierten .class-Dateien und neu
übersetzten generifizierten .class-Dateien.
Nö, diese Details spielen eine ganz wesentliche Rolle, damit ich den
Kram *wirklich verstehe*. Es ist mein Job, anderen Leuten derlei Zeug zu
erklären... da versteht man es gerne etwas genauer, zumindest, wenn man
den Job ernstnimmt. Und ich denke, deshalb gehört es in diese
Generics-FAQ, da ohne diese Information das, was ich oben bzgl. Type
Erasure zitiert habe, genau das bewirkt, was es bei mir bewirkt hat:
Irritation.
|
Naja, wenn du es genau verstehen musst, dann solltest du die Spec lesen
und verstehen lernen. (Da bin ich selbst auch noch meilenweit entfernt.)
Du solltest dich dazu nicht auf FAQs verlassen, die immer irgendwo ungenau
bleiben müssen.
cu |
|
| Back to top |
|
 |
Matthias Ernst Guest
|
Posted: Wed May 16, 2007 8:55 pm Post subject: Re: Type Erasure Tücken |
|
|
On May 16, 2:39 pm, Michael Paap <spamgr...@mpaap.de> wrote:
| Quote: | Dann sollte doch in
X.class keine wie auch immer geartete Information über Interface2
vorliegen. Und dann sollte sich das hier übersetzen lassen:
|
Nicht richtig. In X.class sind noch Attribute kompiliert, die die
generische Originalsignatur
bezeichnen. Ansonsten wuesste man ja nicht mal, *dass* X ueberhaupt
Parameter hat.
Matthias |
|
| Back to top |
|
 |
Michael Paap Guest
|
Posted: Wed May 16, 2007 9:42 pm Post subject: Re: Type Erasure Tücken |
|
|
Ralf Ullrich wrote:
| Quote: | Michael Paap wrote:
X ist hier mit einem Typ T parametrisiert, der zwei Schranken hat. Nach
dem, was ich bzgl. Type Erasure glaubte, verstanden zu haben, sollte im
Bytecode nur die erste Schranke auftauchen und genau das bestätigt mir
auch der Decompiler, der liefert:
Da hast a) etwas falschverstand
|
Ich zitiere mal aus der Generics-FAQ
(file:///D:/HTTrack/Java%20Generics%20FAQ/www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ101):
"When the compiler finds the definition of a generic type or method, it
removes all occurrences of the type parameters and replaces them by
their leftmost bound, or type Object if no bound had been specified."
Wenn ich dich recht verstehe, ist diese Formulierung aber doch
mindestens arg irreführend... oder habe ich wirklich falsche Schlüsse
gezogen?
| Quote: | und b) einen Generics-unaware Decompiler
verwendet.
|
Ich habe Jad verwendet. Hast du einen Tip, was ich verwenden könnte, um
mehr zu sehen?
| Quote: | Die zusätzlichen Constraints sind in der .class-Datei in sogenannten
Attributes enthalten mit denen zum Beispiel auch Debugging-Informationen
in die .class-Datei geschrieben werben; also zugebgeben nicht direkt im
Bytecode plus Methoden-Deskriptoren.
Diese Attribute, es geht bei den Generics um die Signature Attribute
(siehe 4.4.4 in diesem PDF
http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf
), kannst du aber mit Reflection auch selbst auslesen.
|
Jetzt wird es wirklich interessant. Bisher ging ich davon aus, dass
derlei zur Laufzeit nicht möglich ist, weil die entsprechenden
Informationen da schlichtweg nicht mehr vorhanden sind. Aber wenn ich so
etwas lese :
"One of the problems with using reflection for data structure analysis
is that the standard Java collections classes (such as
java.util.ArrayList) have always been "dead-ends" for reflection -- once
you reached a collection class, there was no way to access more details
of the data structure because there was no information about the type of
items contained in the collection. Java 5 changed that with the added
support for generic types and the conversion of all the collections
classes to generic forms that support typing. Java 5 also extended the
reflection APIs to provide access to generic type information at run
time. Together, these changes allow reflection to dig deeper into data
structures than ever before." oder "Despite this identical bytecode, the
type parameter information is recorded in the class format using a new
signature attribute. The JVM records this signature information when
loading a class and makes it available at run time using reflection."
(Quelle http://www-128.ibm.com/developerworks/library/j-cwt11085.html)
dann geht da offenbar doch so einiges...
Mal sehen:
------------------------------------------------------------------------
import java.lang.reflect.*;
class GenericTest {
public static void main(String[] args) {
TypeVariable<?>[] types = X.class.getTypeParameters();
for (int i = 0; i < types.length; i++) {
System.out.println("TypeVariable: " + types[i]);
Type[] bounds = types[i].getBounds();
for (int j = 0; j < bounds.length; j++) {
System.out.println(" Bound: " + bounds[j]);
}
}
}
}
------------------------------------------------------------------------
Ausgabe:
TypeVariable: T
Bound: interface Interface1
Bound: interface Interface2
Ahhhh. Trés chique!
| Quote: | Und in der FAQ wird das glaub ich nicht
erklärt, denn diese Details der Reification spielen nur eine Rolle bei der
Kompatibilität zwischen alten nicht generifizierten .class-Dateien und neu
übersetzten generifizierten .class-Dateien.
|
Nö, diese Details spielen eine ganz wesentliche Rolle, damit ich den
Kram *wirklich verstehe*. Es ist mein Job, anderen Leuten derlei Zeug zu
erklären... da versteht man es gerne etwas genauer, zumindest, wenn man
den Job ernstnimmt. Und ich denke, deshalb gehört es in diese
Generics-FAQ, da ohne diese Information das, was ich oben bzgl. Type
Erasure zitiert habe, genau das bewirkt, was es bei mir bewirkt hat:
Irritation.
Ich denke, ich werde da mal eine Mail schreiben.
| Quote: | Hoffe das macht es etwas klarer, wenn nicht weiterfragen.
|
Hemmungslos. *g*
Gruß,
Michael |
|
| Back to top |
|
 |
Michael Paap Guest
|
Posted: Wed May 16, 2007 10:29 pm Post subject: Re: Type Erasure Tücken |
|
|
Ralf Ullrich wrote:
| Quote: | Naja, die Angelika-Langer-Generics-FAQ befasst sich, soweit ich das sehe,
überwiegend mit den Auswirkungen der Generics für den Programmierer.
|
Klar... aber die Tatsache, dass in meinem Beispielcode die Information
über die Bounds des Typs T nach dem Compilieren noch vorhanden sind, ist
ja für den Programmierer durchaus relevant.
| Quote: | Die meisten Decompiler sind zwar offenbar inzwischen so
angepasst, dass sie das Java 5 Class-File-Format lesen können, ignorieren
aber das Signature Attribut, sind also nach wie vor reine Java 2
Decompiler. (Solange dein Decompiler nicht generifizierten Code ausspuckt,
ignoriert er es.) Würde mich aber auch interessieren, ob es inzwischen
echte Java 5 Decompiler gibt.
|
Bis jetzt habe ich nichts gefunden.
| Quote: | Naja, wenn du es genau verstehen musst, dann solltest du die Spec lesen
und verstehen lernen. (Da bin ich selbst auch noch meilenweit entfernt.)
|
Das tue ich hin und wieder durchaus mal, aber bei den Generics habe ich
mich bisher hauptsächlich auf die Langer-FAQ verlassen.
Na, jetzt habe ich ja dich. ;-)
Danke + Gruß,
Michael |
|
| Back to top |
|
 |
Jochen Theodorou Guest
|
Posted: Wed May 16, 2007 11:04 pm Post subject: Re: Type Erasure Tücken |
|
|
Michael Paap schrieb:
| Quote: | Liebe Leute,
gegeben sei folgender Code:
-----------------------------------------------------------------------
interface Interface1 {
void m1();
}
interface Interface2 {
void m2();
}
class X<T extends Interface1 & Interface2> {
private T a;
X(T a) {
this.a = a;
}
public T getA() {
return a;
}
}
class C implements Interface1 {
public void m1() {}
}
-----------------------------------------------------------------------
X ist hier mit einem Typ T parametrisiert, der zwei Schranken hat. Nach
dem, was ich bzgl. Type Erasure glaubte, verstanden zu haben, sollte im
Bytecode nur die erste Schranke auftauchen und genau das bestätigt mir
auch der Decompiler, der liefert:
|
ich benutze meistens das ASM Framework, da gibt es ein nettes Tool in
Form der Klasse org.objectweb.asm.util.TraceClassVisitor. Die liefert
mir hier:
// class version 49.0 (49)
// access flags 32
// signature <T::LInterface1;:LInterface2;>Ljava/lang/Object;
// declaration: X<T implements Interface1, Interface2>
class X {
// compiled from: Foo.java
// access flags 2
// signature TT;
// declaration: T
private LInterface1; a
// access flags 0
// signature (TT;)V
// declaration: void <init>(T)
<init>(LInterface1;)V
L0
LINENUMBER 21 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
L1
LINENUMBER 22 L1
ALOAD 0
ALOAD 1
PUTFIELD X.a : LInterface1;
L2
LINENUMBER 23 L2
RETURN
MAXSTACK = 2
MAXLOCALS = 2
// access flags 1
// signature ()TT;
// declaration: T getA()
public getA()LInterface1;
L0
LINENUMBER 26 L0
ALOAD 0
GETFIELD X.a : LInterface1;
ARETURN
MAXSTACK = 1
MAXLOCALS = 1
}
die Kommentare signature und declaration zeigen dir an was im bytecode
bezüglich generics steht. Die Methoden/Felder sind also alle auf
interface1 zugeschnitten, aber im Klassenkopf stehen beide interfaces.
Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/ |
|
| Back to top |
|
 |
Sven Köhler Guest
|
Posted: Thu May 17, 2007 7:12 am Post subject: Re: Type Erasure Tücken |
|
|
| Quote: | class X<T extends Interface1 & Interface2> {
|
Ich wusst garnich, das sowas überhaupt geht ...
Abgesehen davon, dass es sich kompilieren lässt - und abgesehen davon,
dass der Compiler im class-file hinterlegt, dass T von Interface1 und
Interface2 abgeleitet sein muss:
Wie sieht dann der Bytecode aus!? Wird dann da T durch Object ersetzt?
Oder wird T durch Interface1 ersetzt? Oder durch Interface2? Oder wie
jetzt? Und geht sowas auch mit z.B. Retroweaver? |
|
| Back to top |
|
 |
Michael Paap Guest
|
Posted: Thu May 17, 2007 7:12 am Post subject: Re: Type Erasure Tücken |
|
|
Sven Köhler wrote:
| Quote: | Wie sieht dann der Bytecode aus!? Wird dann da T durch Object ersetzt?
Oder wird T durch Interface1 ersetzt?
|
Ja, siehe mein Zitat aus der Generics-FAQ.
Gruß,
Michael Paap |
|
| Back to top |
|
 |
Sven Köhler Guest
|
Posted: Thu May 17, 2007 7:13 am Post subject: Re: Type Erasure Tücken |
|
|
| Quote: | Wie sieht dann der Bytecode aus!? Wird dann da T durch Object ersetzt?
Oder wird T durch Interface1 ersetzt?
Ja, siehe mein Zitat aus der Generics-FAQ.
|
Hmm - ja aber "left most" ist in diesem Fall nicht eindeutig.
Von daher müsste man meinen, dass er dann Object nimmt. |
|
| Back to top |
|
 |
Sven Köhler Guest
|
Posted: Thu May 17, 2007 11:40 pm Post subject: Re: Type Erasure Tücken |
|
|
| Quote: | Hmm - ja aber "left most" ist in diesem Fall nicht eindeutig.
Was ist an "leftmost" nicht eindeutig?
|
Na ja ...
T ist abgeleitet von Interface1 und Interface2.
Beides ist "links" von T. Also welchen Zweig soll ich nehmen, wenn ich
nach links gehe?
Gut, irgendwann kommt man so oder so wieder bei Object an. Aber das ist
eine andere Geschichte. |
|
| Back to top |
|
 |
Sven Köhler Guest
|
Posted: Thu May 17, 2007 11:44 pm Post subject: Re: Type Erasure Tuecken |
|
|
| Quote: | Hmm - ja aber "left most" ist in diesem Fall nicht eindeutig.
Ich vermute, daß Angelika Langer die englische Sprache
besser beherrscht und »leftmost« geschrieben hat.
|
"left-most" geht auch.
Ätsch  |
|
| Back to top |
|
 |
Stefan Ram Guest
|
Posted: Fri May 18, 2007 12:04 am Post subject: [en]"leftmost" (was: Type Erasure Tuecken) |
|
|
Newsgroups: de.comp.lang.java,de.etc.sprache.misc
Followup-To: de.etc.sprache.misc
Stefan Ram schrieb:
| Quote: | Ich vermute, daß Angelika Langer die englische Sprache
besser beherrscht und »leftmost« geschrieben hat.
|
Sven Köhler schrieb:
| Quote: | "left-most" geht auch.
|
http://www.onelook.com/?w=left-most
http://www.onelook.com/?w=leftmost
Newsgroups: de.comp.lang.java,de.etc.sprache.misc
Followup-To: de.etc.sprache.misc |
|
| Back to top |
|
 |
Michael Paap Guest
|
Posted: Fri May 18, 2007 2:08 am Post subject: Re: Type Erasure Tücken |
|
|
Sven Köhler wrote:
| Quote: | Beides ist "links" von T. Also welchen Zweig soll ich nehmen, wenn ich
nach links gehe?
|
Wovon auch immer du da redest... es geht schlicht um die Deklaration. Es
wird die im Quellcode am weitesten links stehende Schranke genommen.
Gruß,
Michael |
|
| Back to top |
|
 |
Sven Köhler Guest
|
Posted: Fri May 18, 2007 3:10 am Post subject: Re: Type Erasure Tücken |
|
|
| Quote: | Beides ist "links" von T. Also welchen Zweig soll ich nehmen, wenn ich
nach links gehe?
Wovon auch immer du da redest... es geht schlicht um die Deklaration. Es
wird die im Quellcode am weitesten links stehende Schranke genommen.
|
Ach so! Ich dachte an die Klassenhierarchie. |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|