Sự khác biệt giữa <? mở rộng Foo> và <Foo>


20

Tôi dường như có một sự hiểu lầm về sự khác biệt giữa <Foo><? extends Foo>. Theo hiểu biết của tôi, nếu chúng ta có

ArrayList<Foo> foos = new ArrayList<>();

Điều này chỉ ra rằng các đối tượng kiểu Foocó thể được thêm vào danh sách mảng này. Vì các lớp con Foocũng thuộc loại Foo, chúng cũng có thể được thêm vào mà không có lỗi, như được minh họa bởi

ArrayList<Foo> foos = new ArrayList<>();
foos.add(new Foo());
foos.add(new Bar());

nơi Bar extends Foo.

Bây giờ, nói rằng tôi đã định nghĩa foos

ArrayList<? extends Foo> foos = new ArrayList<>();

Sự hiểu biết hiện tại của tôi là điều này thể hiện some unknown type that extends Foo. Tôi hiểu điều này có nghĩa là bất kỳ đối tượng nào là một lớp con Foocó thể được thêm vào danh sách này; có nghĩa là không có sự khác biệt giữa ArrayList<Foo>ArrayList<? extends Foo>.

Để kiểm tra điều này, tôi đã thử viết đoạn mã sau

ArrayList<? extends Foo> subFoos = new ArrayList<>();
subFoos.add(new Foo());
subFoos.add(new Bar());

nhưng đã được nhắc với lỗi biên dịch sau

no suitable method found for add(Foo)
method java.util.Collection.add(capture#1 of ? extends Foo) is not applicable
(argument mismatch; Foo cannot be converted to capture#1 of ? extends Foo)

no suitable method found for add(Bar)
method java.util.Collection.add(capture#2 of ? extends Bar) is not applicable
(argument mismatch; Bar cannot be converted to capture#2 of ? extends Bar)

Dựa trên sự hiểu biết hiện tại của tôi, tôi có thể thấy lý do tại sao tôi không thể thêm Foomột danh sách vào <? extends Foo>, bởi vì nó không phải là một lớp con của chính nó; nhưng tôi tò mò về lý do tại sao tôi không thể thêm một Bardanh sách.

Đâu là lỗ hổng trong sự hiểu biết của tôi?



1
<? extends Foo>là một lớp học cụ thểchưa biết kéo dài Foo. Một hoạt động với lớp này chỉ hợp pháp nếu nó là hợp pháp cho bất kỳ lớp con nàoFoo .
Thông thường

3
Ồ Bạn càng tìm hiểu nhiều về khái quát của Java, bạn càng thấy khó hiểu hơn.
Mason Wheeler

Câu trả lời:


15

Như bạn đã tự khám phá, một ArrayListtuyên bố như ArrayList<? extends Foo> subFoos = new ArrayList<>();sẽ không hữu ích lắm.

Để thấy sự hữu ích của <? extends T>việc xem xét này:

List<Foo> collect( List<? extends Foo> a1, List<? extends Foo> a2 )
{
    List<Foo> collected = new ArrayList<>();
    collected.addAll( a1 );
    collected.addAll( a2 );
    return collected;
}

mà sau này có thể được sử dụng như sau:

List<Foo> foos = collect( new ArrayList<Foo>(), new ArrayList<Bar>() );

hoặc như sau:

List<Foo> foos = collect( new ArrayList<Bar>(), new ArrayList<Foo>() );

lưu ý rằng không có cách nào ở trên có thể hoạt động nếu collectphương thức đã được khai báo như sau:

List<Foo> collect( List<Foo> a1, List<Foo> a2 )
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.