2016-10-28 22 views
6

Thật khó để giải thích bằng lời, nhưng Java Generics được cho tôi một kết quả bất ngờ. Tôi hy vọng rằng nếu tôi nói một danh sách là loại ? extends Object, tôi có thể lưu trữ bất cứ điều gì trong đó. Do đó, nếu danh sách loại Wrapper<? extends Object>, tôi có thể lưu trữ bất kỳ loại Trình bao bọc nào trong đó. Và cứ thế. Điều đó có ý nghĩa với tôi. Nhưng chúng ta hãy giả sử chúng ta có:Java generics hành vi kỳ lạ

private static class Wrapper<T> { 
    public Wrapper(T t) { /**/ } 
} 

Và tôi muốn một cái gì đó như:

private static final List<Wrapper<Wrapper<? extends Object>>> ls1 = new ArrayList<>(); 

Lưu ý rằng điều này mang lại cho tôi một lỗi:

public static <T> doit(T t) { 
    Wrapper<Wrapper<T>> l1 = new Wrapper<>(new Wrapper<>(t)); 
    ls1.add(l1); // nok 
    // add (Wrapper<Wrapper<? extends java.lang.Object>>) in List 
    // cannot be applied to (Wrapper<Wrapper<T>> 
} 

Nhưng nếu tôi quấn Wrapper trong một thứ Wrapper (rs), sau đó:

private static class C<T> extends Wrapper<Wrapper<T>> { 
    public C(T t) { 
     super(new Wrapper<>(t)); 
    } 
} 

private static final List<C<? extends Object>> ls2 = new ArrayList<>(); 

public static <T> doit(T t) { 
    ls2.add(new C<>(t)); // ok 
} 

Lưu ý rằng đó là điều tương tự; điều này không có ý nghĩa với tôi.

PS. Trong trường hợp của tôi, tôi không làm cho một wrapper wrapper, nhưng một ThreadLocal của một lớp chung chung.

+0

Vì vậy, những gì bạn thực sự muốn là 'Danh sách >> '? – Kayaman

+0

@Kayaman Vâng, nhưng sau đó tôi không thể lưu một Danh sách > trong đó. Tôi muốn có thể lưu trữ bất kỳ loại Wrapper nào >. –

+0

Bạn có thể thay đổi ví dụ của bạn thành một cái gì đó cụ thể, như 'String' như trong bình luận của bạn? Nó ném tôi ra để xem một tờ khai tham chiếu kiểu 'T'. – Zircon

Trả lời

2

Tôi nghĩ

? extends Object ---> equals ? 

Bạn có thể làm như thế này

List<Wrapper<Wrapper<?>>> ls1 = new ArrayList<>(); 

And 

Wrapper<Wrapper<?>> l1 = new Wrapper<>(new Wrapper<>(t)); 
ls1.add(l1); // OK 
0

Thành thật mà nói, tôi sẽ phải xem xét chi tiết hơn tại sao nó không hoạt động. Nó có khả năng nhất liên quan đến tẩy xóa loại chung. Những gì tôi có là một giải pháp thay thế, tôi biết giải pháp này mất đi kiểu chữ cho đối tượng l1. Nếu điều này là ok cho bạn lấy nó.

Wrapper<Wrapper<? extends Object>> l1 = new Wrapper<>(new Wrapper<>(t)); 
ls1.add(l1); 
0

Về cơ bản bạn cần phải quyết định những gì là quan trọng hơn, là chung chung về những gì bạn muốn đặt vào wrapper , trên hoặc những gì bạn lấy ra của trình bao bọc (bạn không thể an toàn cả hai). Sau khi quyết định, bạn sẽ cần phải chọn giữa extendssuper.

Nếu bạn chỉ muốn lưu con trỏ thì List<Wrapper<Wrapper>> sẽ đủ, nhưng sau đó bạn sẽ cần thực hiện một số thao tác sau này.


Khi bạn đang sử dụng extends sau đó ? extends Foo nghĩa là bất kỳ kiểu dữ liệu trở bởi cấu trúc sẽ một subtype của loại Foo (hoặc Foo chính nó).

Ví dụ nếu bạn có void doIt1(List< ? extends Mammal> l ){} bạn có thể vượt qua trong List<Human>, List<Primate>, List<Simian> hoặc một List<Mammal>.

Tuy nhiên, khi sử dụng ? extends bạn không biết những gì là an toàn để đưa vào.

Hãy xem xét điều này:

void addElephant(List< ? extends Mammal> l ){ 
    l.add(new Elephant()); //wrong! 
} 

Đây rõ ràng là không an toàn ! Tôi có thể đã chuyển vào một số List<Human>l và bây giờ danh sách của tôi là Human s có một số Elephant trong đó!

Mặt khác bạn có super nơi ? super Foo nghĩa là container có thể mất một Foo.

Vì vậy, đối void doIt2(List< ? extends Human> l ){} như bạn có thể vượt qua trong List<Human>, List<Primate>, List<Simian>, List<Mammal>, hoặc một List<Object>.

void (List< ? super Human> l ){ 
    l.add(new Human()); 
} 

Và điều này là tốt, nhưng bạn không thể đảm bảo bất cứ điều gì về những gì bạn sẽ ra khỏi vùng chứa.

void (List< ? super Human> l ){ 
    Human h = l.get(0); //wrong! 
} 

Đó là bởi vì nếu l thực sự là một List<Primate> nó có thể có một hỗn hợp của Human s và Gorilla s và không có đảm bảo rằng các yếu tố đầu tiên sẽ là một Human.

Đối với điều này bạn sẽ nhận được từ viết tắt P.E.C.S = "[a] P roducer ** E xtends, [nhưng] C onsumer S Cần mở của".

Nếu thùng chứa đang trả về đối tượng cho phương pháp (ví dụ get), thì bạn là "sản xuất" (vì vậy hãy sử dụng extends), nếu không chấp nhận các yếu tố (ví dụ: add) "tiêu thụ" đối tượng để sử dụng "siêu". (Nb: các phần phức tạp cần nhớ về PECS là nó là từ điểm nhìn của vùng chứa và không phải mã gọi!).

Nếu bạn muốn cả sản xuất và tiêu thụ bạn sẽ cần phải có một loại danh sách bê tông List<Primate> nơi bạn có thể thêm Human s và lấy Primate s.


Xem thêm:

+0

Thành thật mà nói, có vẻ như bạn đã tập trung nhiều nỗ lực vào việc này, nhưng anh ta chỉ muốn giữ an toàn kiểu l1 và viết một bài luận về phương pháp. –

+0

@ mh-dev yeah, tôi muốn giải thích những gì đã sai với cách tiếp cận ngây thơ đối với câu hỏi của mình _and_ như một tham chiếu để có thể chỉ ra cho các thực tập viên và các nhà phát triển cơ sở của chúng tôi. – ArtB