1. Übersicht
Es gibt viele Probleme bei der Verwendung von Java-Generika, z. B. dass List
2. Wie geht der Compiler mit Generika um?
Normalerweise behandelt ein Compiler Generika auf zwei Arten:
1.Code-Spezialisierung. Beim Instanziieren einer generischen Klasse oder generischen Methode wird ein neuer Objektcode (Bytecode oder Binärcode) generiert. Beispielsweise kann es für eine generische Liste erforderlich sein, drei Zielcodes für String, Integer und Float zu generieren.
2.Code-Sharing. Für jede generische Klasse wird nur eine eindeutige Kopie des Zielcodes generiert; alle Instanzen der generischen Klasse werden diesem Zielcode zugeordnet und bei Bedarf werden Typprüfung und Typkonvertierung durchgeführt.
Eine Vorlage in C++ ist eine typische Code-Spezialisierungsimplementierung. Der C++-Compiler generiert einen Ausführungscode für jede generische Klasseninstanz. Ganzzahlliste und Zeichenfolgenliste sind zwei verschiedene Typen im Ausführungscode. Dies führt zu einer Code-Aufblähung, aber erfahrene C++-Programmierer können eine Code-Aufblähung geschickt vermeiden.
Ein weiterer Nachteil der Code-Spezialisierung ist die Platzverschwendung im Referenztypsystem, da die Elemente in der Referenztypsammlung im Wesentlichen Zeiger sind. Es ist nicht erforderlich, für jeden Typ einen Ausführungscode zu generieren. Dies ist auch der Hauptgrund, warum der Java-Compiler Code-Sharing verwendet, um Generics zu verarbeiten.
Der Java-Compiler erstellt durch Code-Sharing eine eindeutige Bytecode-Darstellung für jeden generischen Typ und ordnet Instanzen des generischen Typs dieser eindeutigen Bytecode-Darstellung zu. Die Zuordnung mehrerer generischer Typinstanzen zu einer eindeutigen Bytecode-Darstellung erfolgt durch Typlöschung.
3. Was ist Typlöschung?
Typlöschung bezieht sich auf die Zuordnung generischer Typinstanzen zu demselben Bytecode durch Zusammenführen von Typparametern. Der Compiler generiert nur einen Bytecode für den generischen Typ und ordnet seine Instanzen diesem Bytecode zu. Der Schlüssel zur Typlöschung besteht darin, Informationen über Typparameter aus generischen Typen zu löschen und bei Bedarf Typprüfungs- und Typkonvertierungsmethoden hinzuzufügen.
Typlöschung kann einfach als Konvertierung von generischem Java-Code in gewöhnlichen Java-Code verstanden werden. Der Compiler ist jedoch direkter und wandelt generischen Java-Code direkt in gewöhnlichen Java-Bytecode um.
Der Hauptprozess der Typlöschung ist wie folgt:
1. Ersetzen Sie alle generischen Parameter durch den Typ ihrer äußersten linken Grenze (übergeordneter Typ der obersten Ebene).
2. Entfernen Sie alle Typparameter.
Der Typ von
interface Comparable <A> { public int compareTo( A that); } final class NumericValue implements Comparable <NumericValue> { priva te byte value; public NumericValue (byte value) { this.value = value; } public byte getValue() { return value; } public int compareTo( NumericValue t hat) { return this.value - that.value; } } ----------------- class Collections { public static <A extends Comparable<A>>A max(Collection <A> xs) { Iterator <A> xi = xs.iterator(); A w = xi.next(); while (xi.hasNext()) { A x = xi.next(); if (w.compareTo(x) < 0) w = x; } return w; } } final class Test { public static void main (String[ ] args) { LinkedList <NumericValue> numberList = new LinkedList <NumericValue> (); numberList .add(new NumericValue((byte)0)); numberList .add(new NumericValue((byte)1)); NumericValue y = Collections.max( numberList ); } }<span style="color: #333333; font-family: Arial; font-size: 14px;"> </span>
nach der Typlöschung ist
interface Comparable {
public int compareTo( Object that); } final class NumericValue implements Comparable { priva te byte value; public NumericValue (byte value) { this.value = value; } public byte getValue() { return value; } public int compareTo( NumericValue t hat) { return this.value - that.value; } public int compareTo(Object that) { return this.compareTo((NumericValue)that); } } ------------- class Collections { public static Comparable max(Collection xs) { Iterator xi = xs.iterator(); Comparable w = (Comparable) xi.next(); while (xi.hasNext()) { Comparable x = (Comparable) xi.next(); if (w.compareTo(x) < 0) w = x; } return w; } } final class Test { public static void main (String[ ] args) { LinkedList numberList = new LinkedList(); numberList .add(new NumericValue((byte)0)); , numberList .add(new NumericValue((byte)1)); NumericValue y = (NumericValue) Collections.max( numberList ); } }<span style="color: #333333; font-family: Arial; font-size: 14px;"> </span>
Die erste generische Klasse Comparable ist durch das am weitesten links stehende Begrenzungsobjekt ersetzt. Der Typparameter NumericValue von Comparable
Das zweite Beispiel begrenzt die Grenze der Typparameter > A muss eine Unterklasse von Comparable sein. Gemäß dem Prozess der Typlöschung werden zuerst alle Typparameter besprochen . Ersetzen Sie ti durch die Grenze Comparable ganz links und entfernen Sie dann den Parametertyp A, um das endgültige gelöschte Ergebnis zu erhalten.
4. Durch die Typlöschung verursachte Probleme
Gerade aufgrund der verborgenen Existenz der Typlöschung führt sie direkt zu vielen generischen übernatürlichen Problemen.
F1. Instanzen derselben generischen Klasse verwenden, um Methodensignaturen zu unterscheiden? --NEIN!
import java.util.*;
public class Erasure{ public void test(List<String> ls){ System.out.println("Sting"); } public void test(List<Integer> li){ System.out.println("Integer"); } }<span style="color: #333333; font-family: Arial; font-size: 14px;"> </span>
Kompilieren Sie diese Klasse,
参数类型明明不一样啊,一个List
Q2. 同时catch同一个泛型异常类的多个实例?——NO!
同理,如果定义了一个泛型一场类GenericException
Q3.泛型类的静态变量是共享的?——Yes!
猜猜这段代码的输出是什么?
import java.util.*; public class StaticTest{ public static void main(String[] args){ GT<Integer> gti = new GT<Integer>(); gti.var=1; GT<String> gts = new GT<String>(); gts.var=2; System.out.println(gti.var); } } class GT<T>{ public static int var=0; public void nothing(T x){} }<span style="color: #333333; font-family: Arial; font-size: 14px;"> </span>
答案是——2!由于经过类型擦除,所有的泛型类实例都关联到同一份字节码上,泛型类的所有静态变量是共享的。
五、Just remember
1.虚拟机中没有泛型,只有普通类和普通方法
2.所有泛型类的类型参数在编译时都会被擦除
3.创建泛型对象时请指明类型,让编译器尽早的做参数检查(Effective Java,第23条:请不要在新代码中使用原生态类型)
4.不要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你。
更多Java泛型-类型擦除相关文章请关注PHP中文网!