Le sous-typage est invariant pour les types paramétrés. Même difficile, la classe Dog
est un sous-type de Animal
, le type paramétré List<Dog>
n'est pas un sous-type de List<Animal>
. En revanche, le sous-typage covariant est utilisé par les tableaux, donc le type de tableau Dog[]
est un sous-type deAnimal[]
.
Le sous-typage invariant garantit que les contraintes de type imposées par Java ne sont pas violées. Considérez le code suivant donné par @Jon Skeet:
List<Dog> dogs = new ArrayList<Dog>(1);
List<Animal> animals = dogs;
animals.add(new Cat()); // compile-time error
Dog dog = dogs.get(0);
Comme indiqué par @ Jon Skeet, ce code est illégal, car sinon il violerait les contraintes de type en renvoyant un chat quand un chien s'y attendait.
Il est instructif de comparer ce qui précède au code analogue pour les tableaux.
Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];
Le code est légal. Cependant, lève une exception de magasin de tableaux . Un tableau transporte son type au moment de l'exécution de cette manière, la JVM peut appliquer la sécurité de type du sous-typage covariant.
Pour mieux comprendre cela, regardons le bytecode généré par javap
la classe ci-dessous:
import java.util.ArrayList;
import java.util.List;
public class Demonstration {
public void normal() {
List normal = new ArrayList(1);
normal.add("lorem ipsum");
}
public void parameterized() {
List<String> parameterized = new ArrayList<>(1);
parameterized.add("lorem ipsum");
}
}
En utilisant la commande javap -c Demonstration
, cela montre le bytecode Java suivant:
Compiled from "Demonstration.java"
public class Demonstration {
public Demonstration();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void normal();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
public void parameterized();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: iconst_1
5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
8: astore_1
9: aload_1
10: ldc #4 // String lorem ipsum
12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
17: pop
18: return
}
Observez que le code traduit des corps de méthode est identique. Le compilateur a remplacé chaque type paramétré par son effacement . Cette propriété est cruciale, ce qui signifie qu'elle n'a pas rompu la compatibilité descendante.
En conclusion, la sécurité d'exécution n'est pas possible pour les types paramétrés, car le compilateur remplace chaque type paramétré par son effacement. Cela fait que les types paramétrés ne sont rien d'autre que du sucre syntaxique.