Câu trả lời:
Phương sai:
class Super {
Object getSomething(){}
}
class Sub extends Super {
String getSomething() {}
}
Sub # getSomething là hiệp biến vì nó trả về một lớp con của kiểu trả về Super # getSomething (nhưng điền đầy đủ hợp đồng của Super.getSomething ())
Sự tương phản
class Super{
void doSomething(String parameter)
}
class Sub extends Super{
void doSomething(Object parameter)
}
Sub # doSomething là trái ngược vì nó nhận tham số của một lớp cha của tham số Super # doSomething (nhưng, một lần nữa, điền đầy đủ hợp đồng của Super # doSomething)
Lưu ý: ví dụ này không hoạt động trong Java. Trình biên dịch Java sẽ quá tải và không ghi đè phương thức doSomething () -. Các ngôn ngữ khác cũng hỗ trợ phong cách đối lập này.
Generics
Điều này cũng có thể cho Generics:
List<String> aList...
List<? extends Object> covariantList = aList;
List<? super String> contravariantList = aList;
Bây giờ bạn có thể truy cập vào tất cả các phương thức covariantList
không có tham số chung (vì nó phải là một thứ gì đó "Extended Object"), nhưng getters sẽ hoạt động tốt (vì đối tượng trả về sẽ luôn có kiểu "Object")
Điều ngược lại là đúng đối với contravariantList
: Bạn có thể truy cập tất cả các phương thức có tham số chung (bạn biết nó phải là lớp cha của "String", vì vậy bạn luôn có thể chuyển một), nhưng không có getters (Kiểu trả về có thể thuộc bất kỳ siêu kiểu nào khác của String )
Đồng phương sai: Có thể lặp lại và Trình lặp lại. Hầu như luôn luôn có ý nghĩa khi xác định một biến thể đồng Iterable
hoặc Iterator
. Iterator<? extends T>
có thể được sử dụng giống như Iterator<T>
- nơi duy nhất mà tham số kiểu xuất hiện là kiểu trả về từ next
phương thức, vì vậy nó có thể được truyền lên một cách an toàn T
. Nhưng nếu bạn đã S
mở rộng T
, bạn cũng có thể gán Iterator<S>
cho một biến kiểu Iterator<? extends T>
. Ví dụ: nếu bạn đang xác định một phương pháp tìm:
boolean find(Iterable<Object> where, Object what)
bạn sẽ không thể gọi nó bằng List<Integer>
và 5
, vì vậy nó được định nghĩa tốt hơn là
boolean find(Iterable<?> where, Object what)
Tương phản phương sai: Bộ so sánh. Nó hầu như luôn luôn có ý nghĩa khi sử dụng Comparator<? super T>
, bởi vì nó có thể được sử dụng giống như vậy Comparator<T>
. Tham số kiểu chỉ xuất hiện dưới compare
dạng kiểu tham số phương thức, vì vậy T
có thể được truyền an toàn cho nó. Ví dụ: nếu bạn có một DateComparator implements Comparator<java.util.Date> { ... }
và bạn muốn sắp xếp một List<java.sql.Date>
với bộ so sánh đó ( java.sql.Date
là một lớp con của java.util.Date
), bạn có thể thực hiện với:
<T> void sort(List<T> what, Comparator<? super T> how)
nhưng không phải với
<T> void sort(List<T> what, Comparator<T> how)
Nhìn vào nguyên tắc thay thế Liskov . Trên thực tế, nếu lớp B mở rộng lớp A thì bạn có thể sử dụng B bất cứ khi nào yêu cầu A.
contra variant
nói. super.doSomething("String")
không thể thay thế bằng sub.doSomething(Object)
.