Effacement de type en Java expliqué

1. Vue d'ensemble

Dans cet article rapide, nous aborderons les bases d'un mécanisme important des génériques Java appelé effacement de type.

2. Qu'est-ce que l'effacement de type?

L'effacement de type peut être expliqué comme le processus consistant à appliquer des contraintes de type uniquement au moment de la compilation et à supprimer les informations de type d'élément lors de l'exécution.

Par exemple:

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

Le compilateur remplace le type indépendant E par un type réel d' Object :

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

Par conséquent, le compilateur assure la sécurité de type de notre code et empêche les erreurs d'exécution.

3. Types d'effacement de type

L'effacement de type peut se produire au niveau de la classe (ou de la variable) et de la méthode.

3.1. Effacement de type de classe

Au niveau de la classe, le compilateur rejette les paramètres de type sur la classe et les remplace par sa première liaison, ou Object si le paramètre de type n'est pas lié.

Implémentons une pile en utilisant un tableau:

public class Stack { private E[] stackContent; public Stack(int capacity) { this.stackContent = (E[]) new Object[capacity]; } public void push(E data) { // .. } public E pop() { // .. } }

Lors de la compilation, le compilateur remplace le paramètre de type indépendant E par Object :

public class Stack { private Object[] stackContent; public Stack(int capacity) { this.stackContent = (Object[]) new Object[capacity]; } public void push(Object data) { // .. } public Object pop() { // .. } }

Dans un cas où le paramètre de type E est lié:

public class BoundStack
    
      { private E[] stackContent; public BoundStack(int capacity) { this.stackContent = (E[]) new Object[capacity]; } public void push(E data) { // .. } public E pop() { // .. } }
    

Le compilateur remplacera le paramètre de type lié E par la première classe liée, comparable dans ce cas :

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. Effacement du type de méthode

Pour l'effacement de type au niveau de la méthode, le paramètre de type de la méthode n'est pas stocké mais plutôt converti en son type parent Object s'il est non lié ou sa première classe liée lorsqu'elle est liée.

Considérons une méthode pour afficher le contenu de n'importe quel tableau donné:

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

Lors de la compilation, le compilateur remplace le paramètre de type E par Object :

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

Pour un paramètre de type de méthode lié:

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

Nous ferons effacer le paramètre de type E et le remplacer par Comparable:

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

4. Cas de bord

Au cours du processus d'effacement de type, le compilateur crée une méthode synthétique pour différencier les méthodes similaires. Ceux-ci peuvent provenir de signatures de méthodes étendant la même première classe liée.

Créons une nouvelle classe qui étend notre implémentation précédente de Stack. Veuillez noter que cela fait référence à la classe Stack que nous avons créée dans la section 3.1 , et non à java.util.Stack .

public class IntegerStack extends Stack { public IntegerStack(int capacity) { super(capacity); } public void push(Integer value) { super.push(value); } }

Regardons maintenant le code suivant:

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

Après l'effacement de type, nous avons:

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

Notez comment nous pouvons pousser un tring S sur IntegerStack - parce qu'IntegerStack a hérité de push (Object) de la classe parent Stack . Ceci est, bien sûr, incorrect - car il devrait s'agir d'un entier puisque integerStack est un type Stack .

Donc, sans surprise, une tentative de pop une chaîne et d' attribuer à un nombre entier provoque une ClassCastException d'un casting inséré lors de la poussée par le compilateur.

4.1. Méthodes de pont

Pour résoudre le cas de bord ci-dessus, le compilateur crée parfois une méthode de pont. Il s'agit d'une méthode synthétique créée par le compilateur Java lors de la compilation d'une classe ou d'une interface qui étend une classe paramétrée ou implémente une interface paramétrée où les signatures de méthode peuvent être légèrement différentes ou ambiguës.

In our example above, the Java compiler preserves polymorphism of generic types after erasure by ensuring no method signature mismatch between IntegerStack‘s push(Integer) method and Stack‘s push(Object) method.

Hence, the compiler creates a bridge method here:

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); } }

Consequently, Stack class's push method after type erasure, delegates to the original push method of IntegerStack class.

5. Conclusion

In this tutorial, we've discussed the concept of type erasure with examples in type parameter variables and methods.

You can read more about these concepts:

  • Java Language Specification: Type Erasure
  • The Basics of Java Generics

Comme toujours, le code source qui accompagne cet article est disponible à l'adresse over sur GitHub.