Erase in Java erklärt

Geben Sie Erasure in Java Explained ein

1. Überblick

In diesem kurzen Artikel werden die Grundlagen eines wichtigen Mechanismus in Java-Generika erläutert, der als Typlöschung bezeichnet wird.

2. Was ist Typlöschung?

Das Löschen von Typen kann als der Prozess erklärt werden, bei dem Typbeschränkungen nur zur Kompilierungszeit erzwungen und die Elementtypinformationen zur Laufzeit verworfen werden.

Zum Beispiel:

public static   boolean containsElement(E [] elements, E element){
    for (E e : elements){
        if(e.equals(element)){
            return true;
        }
    }
    return false;
}

Beim Kompilieren wird der ungebundene TypE durch einen tatsächlichen Typ vonObject ersetzt:

public static  boolean containsElement(Object [] elements, Object element){
    for (Object e : elements){
        if(e.equals(element)){
            return true;
        }
    }
    return false;
}

Der Compiler sorgt für die Typensicherheit unseres Codes und verhindert Laufzeitfehler.

3. Arten der Typlöschung

Die Typlöschung kann auf Klassen- (oder Variablen-) und Methodenebene erfolgen.

3.1. Löschen des Klassentyps

Auf Klassenebene werden Typparameter für die Klasse während der Codekompilierung verworfen und durch ihre erste Grenze oderObject ersetzt, wenn der Typparameter nicht gebunden ist.

Implementieren wir einStack mithilfe eines Arrays:

public class Stack {
    private E[] stackContent;

    public Stack(int capacity) {
        this.stackContent = (E[]) new Object[capacity];
    }

    public void push(E data) {
        // ..
    }

    public E pop() {
        // ..
    }
}

Bei der Kompilierung wird der ungebundene TypparameterE durchObject ersetzt:

public class Stack {
    private Object[] stackContent;

    public Stack(int capacity) {
        this.stackContent = (Object[]) new Object[capacity];
    }

    public void push(Object data) {
        // ..
    }

    public Object pop() {
        // ..
    }
}

In einem Fall, in dem der TypparameterE gebunden ist:

public class BoundStack> {
    private E[] stackContent;

    public BoundStack(int capacity) {
        this.stackContent = (E[]) new Object[capacity];
    }

    public void push(E data) {
        // ..
    }

    public E pop() {
        // ..
    }
}

Beim Kompilieren wird der gebundene TypparameterE durch die erste gebundene KlasseComparable ersetzt, in diesem Fall:

public class BoundStack {
    private Comparable [] stackContent;

    public BoundStack(int capacity) {
        this.stackContent = (Comparable[]) new Object[capacity];
    }

    public void push(Comparable data) {
        // ..
    }

    public Comparable pop() {
        // ..
    }
}

3.2. Löschen des Methodentyps

Beim Löschen von Typen auf Methodenebene wird der Typparameter der Methode nicht gespeichert, sondern in den übergeordneten TypObject konvertiert, wenn er ungebunden ist, oder in die erste gebundene Klasse, wenn er gebunden ist.

Betrachten wir eine Methode zum Anzeigen des Inhalts eines bestimmten Arrays:

public static  void printArray(E[] array) {
    for (E element : array) {
        System.out.printf("%s ", element);
    }
}

Bei der Kompilierung wird der TypparameterE durchObject ersetzt:

public static void printArray(Object[] array) {
    for (Object element : array) {
        System.out.printf("%s ", element);
    }
}

Für einen gebundenen Methodentypparameter:

public static > void printArray(E[] array) {
    for (E element : array) {
        System.out.printf("%s ", element);
    }
}

Der TypparameterEwird gelöscht und durchComparable:ersetzt

public static void printArray(Comparable[] array) {
    for (Comparable element : array) {
        System.out.printf("%s ", element);
    }
}

4. Randfälle

Irgendwann während des Löschvorgangs erstellt der Compiler eine synthetische Methode, um ähnliche Methoden zu unterscheiden. Diese können von Methodensignaturen stammen, die dieselbe erste gebundene Klasse erweitern.

Erstellen wir eine neue Klasse, die unsere vorherige Implementierung vonStack erweitert:

public class IntegerStack extends Stack {

    public IntegerStack(int capacity) {
        super(capacity);
    }

    public void push(Integer value) {
        super.push(value);
    }
}

Schauen wir uns nun den folgenden Code an:

IntegerStack integerStack = new IntegerStack(5);
Stack stack = integerStack;
stack.push("Hello");
Integer data = integerStack.pop();

Nach dem Löschen des Typs haben wir:

IntegerStack integerStack = new IntegerStack(5);
Stack stack = (IntegerStack) integerStack;
stack.push("Hello");
Integer data = (String) integerStack.pop();

Beachten Sie, wie wirString aufIntegerStack verschieben können - weilIntegerStackpush(Object) von der übergeordneten KlasseStack geerbt hat. Dies ist natürlich falsch - da es sich um eine Ganzzahl handeln sollte, daintegerStack einStack<Integer>-Typ ist.

Es überrascht also nicht, dass ein Versuch,pop aString undInteger zuzuweisen, aClassCastException aus einem Cast verursacht, der während derpush vom Compiler eingefügt wurde.

4.1. Brückenmethoden

Um den obigen Randfall zu lösen, erstellt der Compiler manchmal eine Brückenmethode. Hierbei handelt es sich um eine synthetische Methode, die vom Java-Compiler beim Kompilieren einer Klasse oder Schnittstelle erstellt wurde, die eine parametrisierte Klasse erweitert oder eine parametrisierte Schnittstelle implementiert, bei der die Methodensignaturen möglicherweise geringfügig voneinander abweichen oder nicht eindeutig sind.

In unserem obigen Beispiel behält der Java-Compiler den Polymorphismus generischer Typen nach dem Löschen bei, indem sichergestellt wird, dass zwischenIntegerStackpush(Integer)undStackpush(Object)keine Methode vorliegt.

Daher erstellt der Compiler hier eine Brückenmethode:

public class IntegerStack extends Stack {
    // Bridge method generated by the compiler

    public void push(Object value) {
        push((Integer)value);
    }

    public void push(Integer value) {
        super.push(value);
    }
}

Folglich delegiert diepush-Methode derStack-Klasse nach dem Löschen des Typs an die ursprünglichepush-Methode derIntegerStack-Klasse.

5. Fazit

In diesem Lernprogramm haben wir das Konzept der Typlöschung anhand von Beispielen für Typparametervariablen und -methoden erläutert.

Sie können mehr über diese Konzepte lesen:

Wie immer ist der diesem Artikel beiliegende Quellcodeover on GitHub verfügbar.