2010-03-23 40 views

Trả lời

119

Hiệp 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ề của Siêu # getSomething (nhưng fullfills hợp đồng Super.getSomething())

Contravariance

class Super{ 
    void doSomething(String parameter) 
} 
class Sub extends Super{ 
    void doSomething(Object parameter) 
} 

Sub # doSomething là contrava riant bởi vì nó có một tham số của một superclass của tham số của Super # doSomething (nhưng, một lần nữa, fullfills 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() - Method. Các ngôn ngữ khác hỗ trợ kiểu đối nghịch này.

Generics

Đây cũng là 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 pháp covariantList mà không mất một tham số chung (vì nó phải được một cái gì đó "mở rộng đối tượng"), nhưng getters sẽ hoạt động tốt (vì đối tượng trả về sẽ luôn là loại "Đối tượng")

Điều ngược lại là đúng với contravariantList: Bạn có thể truy cập tất cả các phương thức với các tham số chung (bạn biết nó phải là siêu lớp của "Chuỗi", vì vậy bạn luôn có thể vượt qua một), nhưng không có getters (Loại trả về có thể là bất kỳ siêu loại nào khác của Chuỗi)

+68

Ví dụ đầu tiên về contravariance không hoạt động trong Java. doSomething() trong lớp Sub là quá tải, không phải là ghi đè. –

+13

Thật vậy. Java không hỗ trợ đối số contravariant trong subtyping. Chỉ hiệp phương sai cho các kiểu trả về phương thức quan tâm (như trong ví dụ đầu tiên). –

+0

Câu trả lời hay. Hiệp phương sai có vẻ hợp lý với tôi. Nhưng bạn có thể chỉ cho tôi một đoạn trong JLS mô tả contravariance? Tại sao Sub.doSomething được gọi? – Mikhail

1

Nhìn vào số Liskov substitution principle. Trong thực tế, nếu lớp B mở rộng lớp A thì bạn sẽ có thể sử dụng B bất cứ khi nào A được yêu cầu.

+2

này không trả lời câu hỏi và gây hiểu nhầm. Sẽ hoàn toàn có thể thiết kế một hệ thống biến thể phá vỡ tính chính xác ngữ nghĩa và do đó vi phạm LSP. –

+0

đây không phải là trường hợp của 'biến thể contra'. 'super.doSomething (" String ")' không thể được thay thế bằng 'sub.doSomething (Object)'. – zinking

38

Đồng phương sai: Iterable và Iterator. Nó hầu như luôn luôn có ý nghĩa để xác định một biến thể Iterable hoặc Iterator. Iterator<? extends T> có thể được sử dụng chỉ là Iterator<T> - nơi duy nhất mà thông số loại xuất hiện là loại trả về từ phương thức next, do đó, nó có thể được chuyển an toàn lên T. Nhưng nếu bạn có S mở rộng T, bạn cũng có thể gán Iterator<S> cho một biến loại 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ó với List<Integer>5, vì vậy nó tốt hơn định nghĩa là

boolean find(Iterable<?> where, Object what) 

Contra-sai: sánh. Nó hầu như luôn luôn có ý nghĩa để sử dụng Comparator<? super T>, bởi vì nó có thể được sử dụng giống như Comparator<T>. Thông số loại chỉ xuất hiện dưới dạng loại tham số phương thức compare, vì vậy, T có thể được chuyển an toàn đến thông số đó.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 so sánh đó (java.sql.Date là một sub-class của java.util.Date), bạn có thể làm 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) 
Các vấn đề liên quan