Java Generics (Ký tự đại diện)


109

Tôi có một số câu hỏi về các ký tự đại diện chung trong Java:

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

  2. Ký tự đại diện có giới hạn là gì và ký tự đại diện không giới hạn là gì?


Trong trường hợp câu trả lời của Ted Gao bị xóa (vì nó chỉ ở dạng liên kết), đây là bài đăng trên blog mà nó đã liên kết.
royhowie

Câu trả lời:


123

Trong câu hỏi đầu tiên của bạn, <? extends T><? super T>là ví dụ về các ký tự đại diện có giới hạn. Một ký tự đại diện không bị ràng buộc trông như thế nào <?>và về cơ bản có nghĩa là <? extends Object>. Nó có nghĩa là chung chung có thể là bất kỳ loại nào. Một ký tự đại diện bị giới hạn ( <? extends T>hoặc <? super T>) đặt giới hạn đối với loại bằng cách nói rằng nó phải 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ề các generic trong các bài viết Ký tự đại diệnThú vị hơn với Ký tự đại diện .


Chỉ cần hiểu đúng, nếu A <B và B <C thì: <A mở rộng C> là sai?
Pablo Fernandez

Nếu A <B, ý bạn là A mở rộng B, thì A mở rộng C. Tuy nhiên, bạn sẽ không sử dụng điều đó trong cú pháp ký tự đại diện, bạn sẽ nói <? mở rộng C> để hạn chế sự lựa chọn của bạn hoặc là A hoặc B.
Bill the Lizard

và trong trường hợp đó nếu tôi nói <? super C> sự khác biệt sẽ là gì?
Pablo Fernandez,

3
Chỉ muốn khuyên khác tham khảo trên Java Generics: angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Zach Scrivena

3
@Pablo: <? super C>có nghĩa là loại của bạn bị hạn chế ở một số thứ ở trên Ctrong hệ thống phân cấp loại. (Xin lỗi vì câu trả lời rất muộn. Tôi đoán chúng tôi đã không có thông báo nhận xét cách đây 2 năm?)
Bill the Lizard

49

Nếu bạn có phân cấp lớp A, B là lớp con của A, và C và D đều là lớp con của B như bên dưới

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ý tự đại diện có giới hạn giống như ? extends Btrong đó B là một số loại. Đó là, loại không xác định nhưng có thể đặt một "ràng buộc" trên đó. Trong trường hợp này, nó được giới hạn bởi một số lớp, đó là một lớp con của B.


Tôi giả sử List<? super B>mô tả là Danh sách chấp nhận kiểu là lớp cha của lớp B ? Đó là lý do tại sao C và D sẽ không biên dịch hmm?
Volkan Güven

38

Josh Bloch cũng giải thích tốt về thời điểm sử dụng superextendstrong cuộc trò chuyện video trên google io này , anh ấy đề cập đến thuật ngữ Người extendstiêu dùng của Nhà sản xuấtsuper .

Từ các trang trình bày:

Giả sử bạn muốn thêm các phương thức hàng loạt vào Stack<E>

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

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

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

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


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.
Pablo Fernandez,

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

2
Giường - tại sao không đưa ví dụ vào câu trả lời này để làm cho nó hoàn chỉnh và độc lập. Liên kết là phù du.
James Schek,

3

Có thể có những lúc bạn muốn hạn chế các loại kiểu được phép chuyển cho tham số kiểu. 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 thể hiện của Số hoặc các lớp con của nó. Đây là những gì các tham số kiểu bị giới hạn dành cho.

Collection<? extends MyObject> 

có nghĩa là nó có thể chấp nhận tất cả các đối tượng có quan hệ IS- A với MyObject (tức là 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 thuộc 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ủa MyObject (tức là bất kỳ đối tượng nào thuộc loại OurObject hoặc YourObject hoặc MyObject, nhưng không chấp nhận bất kỳ đối tượng nào thuộc lớp cha của MyObject).


1

Nói chung,

Nếu một cấu trúc chứa các phần tử có kiểu biểu mẫu ? extends E, chúng ta có thể lấy các phần tử ra khỏi cấu trúc, nhưng không thể đưa các phần 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]"); 

Để đưa các phần tử vào cấu trúc, chúng ta 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));
         }
    }

1

Các ký tự đại diện chung được tạo để làm cho các phương thức hoạt động trên Bộ sưu tập có thể tái sử dụng nhiều hơn.

Ví dụ, nếu một phương thức có một tham số List<A>, chúng ta chỉ có thể cấp cho List<A>phương thức này. Trong một số trường hợp, thật là lãng phí cho việc thực hiện phương pháp này :

  1. Nếu phương thức này chỉ đọc các đối tượng từ List<A>, thì chúng ta nên được phép cấp cho List<A-sub>phương thức này. (Vì A-sub LÀ A)
  2. Nếu phương thức này chỉ chèn các đối tượng vào List<A>, thì chúng ta nên cho phép List<A-super>phương thức này. (Vì A LÀ A-super)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.