Subtyping là bất biến cho các loại tham số. Ngay cả lớp khó khăn Dog
là một kiểu con của Animal
, kiểu tham số List<Dog>
không phải là kiểu con của List<Animal>
. Ngược lại, phân nhóm covariant được sử dụng bởi các mảng, vì vậy kiểu mảng Dog[]
là một kiểu con củaAnimal[]
.
Phân nhóm bất biến đảm bảo rằng các ràng buộc kiểu được thi hành bởi Java không bị vi phạm. Hãy xem xét mã sau đây được đưa ra bởi @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);
Như tuyên bố của @Jon Skeet, mã này là bất hợp pháp, bởi vì nếu không, nó sẽ vi phạm các ràng buộc loại bằng cách trả lại một con mèo khi một con chó mong đợi.
Đó là hướng dẫn để so sánh ở trên với mã tương tự cho các mảng.
Dog[] dogs = new Dog[1];
Object[] animals = dogs;
animals[0] = new Cat(); // run-time error
Dog dog = dogs[0];
Mã này là hợp pháp. Tuy nhiên, ném một ngoại lệ cửa hàng mảng . Một mảng mang kiểu của nó trong thời gian chạy theo cách này JVM có thể thực thi sự an toàn của kiểu phân nhóm covariant.
Để hiểu rõ hơn về điều này, chúng ta hãy nhìn vào mã byte được tạo bởi javap
lớp bên dưới:
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");
}
}
Sử dụng lệnh javap -c Demonstration
, điều này cho thấy mã byte Java sau:
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
}
Quan sát rằng mã dịch của các thân phương thức là giống hệt nhau. Trình biên dịch thay thế từng loại tham số bằng cách xóa . Thuộc tính này rất quan trọng có nghĩa là nó không phá vỡ tính tương thích ngược.
Tóm lại, an toàn thời gian chạy là không thể đối với các loại tham số hóa, vì trình biên dịch thay thế từng loại tham số hóa bằng cách xóa nó. Điều này làm cho các loại tham số hóa không có gì nhiều hơn đường cú pháp.