Hallo, ich lerne Java im Selbststudium mit einem Buch und hätte eine Verständnisfrage zum Thema Type Erasure in Java.
Ich wäre sehr froh, wenn du mir helfen könntest/Sie mir helfen könnten:
So viel ich über Type Erasure in Java weiß, werden nach oben beschränkte Wildcard-Typen durch die "Upper Bound" - die obere Grenze - ersetzt
und aus der "Lower Bound" wird Object.
Wie funktioniert das genau, dass generische Typen so umgewandelt werden, dass die Abwärtskompatibilität gewährleistet ist?
Müssen Casts hinzugefügt werden?
So viel ich über Type Erasure in Java weiß, werden nach oben beschränkte Wildcard-Typen durch die "Upper Bound" - die obere Grenze - ersetzt
und aus der "Lower Bound" wird Object.
[CODE lang="java" title="Type-Erasure"]// der Typ-Parameter ist im class-File enthalten, kein Type-Erasure
class A<T> {
}
class B
// das Typ-Argument ist im class-File enthalten, kein Type-Erasure
extends A<String> {
}
class C {
// das Typ-Argument ist im class-File enthalten, kein Type-Erasure
List<String> fieldStringList;
void myMethod(
// das Typ-Argument ist im class-File enthalten, kein Type-Erasure
List<String> paramStringList) {
// das Typ-Argument ist nicht im class-File enthalten, Type-Erasure
List<String> localStringList = new ArrayList<>();
}
}[/CODE]
An meinem Code-Beispiel siehst Du, dass Type-Erasure nur in executable-Blöcken (Methoden, Konstruktoren, Initializer) stattfindet.
Jein, das Erasure von List<? extends Number> ist schlicht List. Der Compiler stellt sicher, dass es sich um eine Liste eines festen aber unbekannten Typs handelt, der von Number abgeleitet ist. Dadurch, dass der Typ nicht näher bekannt ist, kannst Du z. B. keine Werte hinzufügen. Du kannst aber z. B. über die Liste iterieren und die Methoden von Number verwenden.
Wie funktioniert das genau, dass generische Typen so umgewandelt werden, dass die Abwärtskompatibilität gewährleistet ist?
Müssen Casts hinzugefügt werden?
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.
Jein, das Erasure von List<? extends Number> ist schlicht List. Der Compiler stellt sicher, dass es sich um eine Liste eines festen aber unbekannten Typs handelt, der von Number abgeleitet ist. Dadurch, dass der Typ nicht näher bekannt ist, kannst Du z. B. keine Werte hinzufügen. Du kannst aber z. B. über die Liste iterieren und die Methoden von Number verwenden.
Der Compiler kennt ja Deinen Code. Wenn Du also versuchst, eine List<String> an eine List<? extends Number> zuzuweisen, weiß der Compiler anhand der Objekthierarchie, dass String nicht kompatibel zu Number ist. Wenn Du dagegen eine List<Double> rüberschiebst, ist das ok, da Double von Number abgeleitet ist.
Java:
List<?extendsNumber> list;
list =newArrayList<String>();// Compilerfehler: String ist nicht von Number abgeleitet
list =newArrayList<Double>();// ok, da Double ein von Number abgeleiteter Typ ist
Der Compiler kennt ja Deinen Code. Wenn Du also versuchst, eine List<String> an eine List<? extends Number> zuzuweisen, weiß der Compiler anhand der Objekthierarchie, dass String nicht kompatibel zu Number ist. Wenn Du dagegen eine List<Double> rüberschiebst, ist das ok, da Double von Number abgeleitet ist.
Java:
List<?extendsNumber> list;
list =newArrayList<String>();// Compilerfehler: String ist nicht von Number abgeleitet
list =newArrayList<Double>();// ok, da Double ein von Number abgeleiteter Typ ist
Das ist korrekt. Alle Deklarationen, die potenziell Schnittstellen zu anderen Libraries/Anwendungen sein könnten (Konstruktorsignatur, Methodensignatur, sowie Instanz- und Klassenfelder) behalten ihre generischen Typinformationen. Das hat den Hintergrund, dass beim Einbinden von bereits nur noch als .class Dateien vorhandenen externen Libraries (wie z.B. die JRE Class Library) der Compiler in deinem Sourcecode, der die externe Library verwendet, auch noch Typfehler bezüglich generischer Typen identifizieren kann.
Wenn du also eine Library mit einer Klasse und einer Methode baust, die nur eine List<Integer> als Parameter bekommen darf, diese Klasse dann kompilierst und jemand anderem gibst, der sie bei sich im Classpath einbindet, dann wird dort auch noch sichergestellt, dass deine Methode auch ein List<Integer> bekommt.