2008-10-30 88 views
89

Tôi có một vài câu hỏi về các kí hiệu chung trong Java:Java Generics (ký tự đại diện)

  1. sự khác biệt giữa List<? extends T>List<? super T> là gì?

  2. Ký tự đại diện bị chặn là gì và ký tự đại diện không bị ràng buộc là gì?

+0

Trong trường hợp đó [Ted Gao của] (http://stackoverflow.com/users/970122/ted-gao) [câu trả lời] (http://stackoverflow.com/a/9446233/2476755) bị xóa (vì nó chỉ là liên kết), đây là [bài đăng blog] (http://ted-gao.blogspot.com/search/label/Java%20Generics) mà nó được liên kết . – royhowie

Trả lời

104

Trong câu hỏi đầu tiên, <? extends T><? super T> là ví dụ về ký tự đại diện bị chặn. Một ký tự đại diện không bị ràng buộc trông giống như <?> và về cơ bản có nghĩa là <? extends Object>. Nó có nghĩa là loại generic có thể là bất kỳ loại nào. Một ký tự đại diện bị chặn (<? extends T> hoặc <? super T>) đặt một giới hạn đối với loại này bằng cách cho biết rằng phải có mở rộng một loại cụ thể (<? extends T> được gọi là giới hạn trên) hoặc phải là tổ tiên của một loại cụ thể (<? super T>) được gọi là giới hạn dưới).

Hướng dẫn Java có một số giải thích khá tốt về generics trong các bài viết WildcardsMore Fun with Wildcards.

+0

Chỉ cần làm đúng, nếu A là sai? –

+0

Nếu bằng A để giới hạn lựa chọn của bạn là A hoặc B. –

+0

và trong đó trường hợp nếu tôi nói sự khác biệt là gì? –

42

Nếu bạn có một hệ thống phân cấp lớp A, B là một lớp con của A, và C và D cả hai đều là lớp con của B như dưới đây

class A {} 
class B extends A {} 
class C extends B {} 
class D extends B {} 

Sau đó

List<? extends A> la; 
la = new ArrayList<B>(); 
la = new ArrayList<C>(); 
la = new ArrayList<D>(); 

List<? super B> lb; 
lb = new ArrayList<A>(); //fine 
lb = new ArrayList<C>(); //will not compile 

public void someMethod(List<? extends B> lb) { 
    B b = lb.get(0); // is fine 
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B 
} 

public void otherMethod(List<? super B> lb) { 
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A 
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
} 

Một kí hiệu wildcard bao cũng giống như ? extends B trong đó B là một số loại. Đó là, loại không rõ nhưng một "ràng buộc" có thể được đặt trên đó. Trong trường hợp này, nó được bao quanh bởi một số lớp, mà là một lớp con của B.

+1

+1 cho ví dụ! –

+1

ví dụ tuyệt vời, cảm ơn bạn – Peter

36

Josh Bloch cũng có một lời giải thích tốt khi sử dụng superextends trong google io video talk này nơi ông đề cập đến Nhà sản xuất extends Consumer super mnemonic .

Từ các slide thuyết trình:

Giả sử bạn muốn thêm các phương pháp số lượng lớn cho Stack<E>

void pushAll(Collection<? extends E> src);

- src là một nhà sản xuất E

void popAll(Collection<? super E> dst);

- dst là một người tiêu dùng E

+0

Tôi đã đọc cuốn sách của Bloch, nhưng tôi vẫn không thể thấy sự khác biệt giữa mở rộng và siêu trong trường hợp cụ thể này. –

+0

Xem video, tôi nghĩ nó khá rõ ràng. Ngoài ra, tôi nghĩ bạn nên hỏi một câu hỏi khác về điều này "sự khác biệt giữa Danh sách và Danh sách " nơi bạn hy vọng sẽ nhận được nhiều câu trả lời hơn. (nếu bạn có, hãy thêm một liên kết từ đây) – blank

+2

Giường - tại sao không bao gồm ví dụ trong câu trả lời này để hoàn thành và đứng một mình. Liên kết là tạm thời. –

2

Có thể đôi khi bạn sẽ muốn hạn chế các loại được phép chuyển cho thông số loại. Ví dụ, một phương thức hoạt động trên các số có thể chỉ muốn chấp nhận các cá thể của Number hoặc các lớp con của nó. Đây là những thông số kiểu được bao bọc cho.

Collection<? extends MyObject> 

có nghĩa là nó có thể chấp nhận tất cả đối tượng người có IS-Một mối quan hệ với MyObject (ví dụ:bất kỳ đối tượng nào là một loại myObject hoặc chúng ta có thể nói bất kỳ đối tượng nào của bất kỳ lớp con nào của MyObject) hoặc một đối tượng của lớp MyObject.

Ví dụ:

class MyObject {} 

class YourObject extends MyObject{} 

class OurObject extends MyObject{} 

Sau đó,

Collection<? extends MyObject> myObject; 

sẽ chỉ chấp nhận MyObject hoặc con cái của MyObject (tức là bất kỳ đối tượng kiểu OurObject hoặc YourObject hoặc MyObject, nhưng không phải bất kỳ đối tượng của lớp cha của MyObject).

1

Nói chung,

Nếu một cấu trúc chứa các yếu tố với một loại hình thức ? extends E, chúng tôi có thể nhận được các yếu tố ra khỏi cơ cấu, nhưng chúng tôi không thể đặt yếu tố vào cấu trúc

List<Integer> ints = new ArrayList<Integer>(); 
ints.add(1); 
ints.add(2); 
List<? extends Number> nums = ints; 
nums.add(3.14); // compile-time error 
assert ints.toString().equals("[1, 2, 3.14]"); 

Để đặt các yếu tố vào cấu trúc, chúng tôi cần một loại ký tự đại diện khác được gọi là Wildcards with super,

List<Object> objs = Arrays.<Object>asList(2, 3.14, "four"); 
    List<Integer> ints = Arrays.asList(5, 6); 
    Collections.copy(objs, ints); 
    assert objs.toString().equals("[5, 6, four]"); 

    public static <T> void copy(List<? super T> dst, List<? extends T> src) { 
      for (int i = 0; i < src.size(); i++) { 
       dst.set(i, src.get(i)); 
     } 
    } 
0

Ký tự đại diện chung được tạo để tạo các phương thức hoạt động trên Bộ sưu tập có thể sử dụng lại nhiều hơn. Ví dụ: nếu phương thức có thông số List<A>, chúng tôi chỉ có thể cung cấp cho List<A> phương pháp này. Đó là một sự lãng phí cho Chức năng của phương pháp này trong một số trường hợp:

  1. Nếu phương pháp này chỉ đọc các đối tượng từ List<A>, sau đó chúng ta nên được phép cung cấp cho List<A-sub> đến phương pháp này. (Vì A-sub LÀ A)
  2. Nếu phương pháp này chỉ chèn đối tượng vào List<A>, thì chúng tôi phải được phép cung cấp List<A-super> cho phương pháp này. (Bởi vì A là A-super)
0

Ví dụ, chúng tôi đã sau phân cấp lớp

Object < - Một < - B, C

Bạn nên sử dụng List <? extends A> (trên bị ràng buộc) nếu bạn định truy cập đọc từ danh sách

Khi bạn biết rằng các cá thể trong bộ sưu tập là các cá thể của A hoặc các lớp con của A, nó là an toàn để đọc các phiên bản của bộ sưu tập và đưa chúng vào các phiên bản A.

Bạn không thể chèn thành phần vào danh sách, vì bạn không biết danh sách có được nhập vào loại A, B hay C.

Bạn nên sử dụng List <? super A> (thấp hơn giới hạn) nếu bạn đang đi để chèn vào danh sách

Khi bạn biết rằng danh sách được đánh máy hoặc là A, hoặc một lớp cha của A, nó là an toàn để chèn trường hợp của A hoặc các lớp con của A (ví dụ: B hoặc C) vào danh sách.

Bạn không thể đọc từ danh sách, ngoại trừ nếu nó biểu diễn đối tượng đã đọc đối tượng. Các phần tử đã có trong danh sách có thể thuộc bất kỳ kiểu nào là A hoặc siêu lớp của A, nhưng không thể biết chính xác lớp đó là gì.

đọc thêm ở đây - http://tutorials.jenkov.com/java-generics/wildcards.html

Các vấn đề liên quan