Tôi có một số câu hỏi về các ký tự đại diện chung trong Java:
Sự khác biệt giữa
List<? extends T>
và làList<? super T>
gì?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ì?
Tôi có một số câu hỏi về các ký tự đại diện chung trong Java:
Sự khác biệt giữa List<? extends T>
và là List<? super T>
gì?
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ì?
Câu trả lời:
Trong câu hỏi đầu tiên của bạn, <? extends T>
và <? 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ện và Thú vị hơn với Ký tự đại diện .
<? super C>
có nghĩa là loại của bạn bị hạn chế ở một số thứ ở trên C
trong 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?)
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 B
trong đó 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.
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?
Josh Bloch cũng giải thích tốt về thời điểm sử dụng super
và extends
trong cuộc trò chuyện video trên google io này , anh ấy đề cập đến thuật ngữ Người extends
tiê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
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).
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));
}
}
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 :
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)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)