パラメータ化された型の場合、サブタイピングは不変です。クラスDog
がのサブタイプであるAnimal
としても、パラメータ化されたタイプList<Dog>
はのサブタイプではありませんList<Animal>
。対照的に、共変サブタイプは配列で使用されるため、配列型Dog[]
はのサブタイプですAnimal[]
。
不変のサブタイピングにより、Javaによって強制される型制約に違反しないことが保証されます。@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);
@Jon Skeetによって述べられているように、このコードは違法です。
上記を配列の類似のコードと比較することは有益です。
Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];
コードは合法です。ただし、配列ストア例外をスローします。配列は、JVMが共変サブタイプのタイプセーフを実施できるように、実行時にそのタイプを保持します。
これをさらに理解するためjavap
に、以下のクラスによって生成されたバイトコードを見てみましょう。
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");
}
}
コマンドを使用するとjavap -c Demonstration
、次のJavaバイトコードが表示されます。
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
}
メソッド本体の変換されたコードが同一であることを確認します。コンパイラーは、パラメーター化された各タイプを消去で置き換えました。このプロパティは、下位互換性を損なうものではなかったことを意味します。
結論として、コンパイラはパラメータ化された各型を消去によって置き換えるため、パラメータ化された型の実行時の安全性は不可能です。これにより、パラメーター化された型は構文糖にすぎません。