Heim  >  Artikel  >  Java  >  Java Generics – Typlöschung

Java Generics – Typlöschung

高洛峰
高洛峰Original
2016-12-19 15:35:051457Durchsuche

1. Übersicht

Es gibt viele Probleme bei der Verwendung von Java-Generika, z. B. dass List.class nicht vorhanden ist und List nicht zugewiesen werden kann. Seltsame ClassCastException usw. Die korrekte Verwendung von Java-Generika erfordert ein tiefgreifendes Verständnis einiger Java-Konzepte, wie z. B. Kovarianz, Überbrückungsmethoden und Typlöschung, wie in diesem Hinweis dokumentiert. Fast alle Java-Generika werden im Compiler verarbeitet. Der vom Compiler generierte Bytecode enthält keine generischen Informationen. Dieser Vorgang wird als Typlöschung bezeichnet.

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 wurde gelöscht, aber dies führte direkt dazu, dass NumericValue die Methode CompareTo(Object that) der Schnittstelle Comparable nicht implementierte, sodass der Compiler wie ein guter Kerl agierte und eine Brückenmethode hinzufügte.
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,

Java Generics – Typlöschung

参数类型明明不一样啊,一个List,一个是List,但是,偷偷的说,type erasure之后,它就都是List了⋯⋯
Q2. 同时catch同一个泛型异常类的多个实例?——NO!
同理,如果定义了一个泛型一场类GenericException,千万别同时catch GenericException和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中文网!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn