[英]Java generics weird behaviour
用語言很難解釋,但是Java泛型給了我意外的結果。 我期望如果我說列表是類型的? extends Object
? extends Object
,我可以在其中存儲任何內容。 因此,如果類型為Wrapper<? extends Object>
的列表 Wrapper<? extends Object>
,我可以在其中存儲任何類型的包裝器。 等等。 這對我來說很有意義。 但讓我們假設我們有:
private static class Wrapper<T> {
public Wrapper(T t) { /**/ }
}
我想要這樣的東西:
private static final List<Wrapper<Wrapper<? extends Object>>> ls1 = new ArrayList<>();
請注意,這給了我一個錯誤:
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>>
}
但是,如果我將包裝器包裝在輔助包裝器(rs)中,則:
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
}
注意這是同一回事。 這對我來說毫無意義。
PS。 就我而言,我不是在做包裝器,而是在泛型類的ThreadLocal。
我認為
? extends Object ---> equals ?
你可以這樣
List<Wrapper<Wrapper<?>>> ls1 = new ArrayList<>();
And
Wrapper<Wrapper<?>> l1 = new Wrapper<>(new Wrapper<>(t));
ls1.add(l1); // OK
老實說,我將不得不更詳細地研究為什么它不起作用。 它最有可能與通用類型擦除有關。 我有一個解決方法,我知道此解決方案會失去l1對象的類型。 如果可以,請接受。
Wrapper<Wrapper<? extends Object>> l1 = new Wrapper<>(new Wrapper<>(t));
ls1.add(l1);
基本上,您需要確定更重要的是什么,要對要放入包裝器中的內容,包裝內容或從包裝器中取出的內容進行泛型(您不能安全地兼顧兩者)。 確定后,您將需要在extends
和super
之間進行選擇。
如果只想存儲指針,則List<Wrapper<Wrapper>>
就足夠了,但是稍后您需要進行一些轉換。
當您使用extends
? extends Foo
? extends Foo
意味着結構返回的任何數據類型都將是Foo
類型的子類型(或Foo
本身)。
例如,如果您有void doIt1( List< ? extends Mammal> l ){}
,則可以傳入List<Human>
, List<Primate>
, List<Simian>
或List<Mammal>
。
但是,當使用? extends
? extends
您不知道什么是安全的。
考慮一下:
void addElephant( List< ? extends Mammal> l ){
l.add( new Elephant() ); //wrong!
}
這顯然是不安全的 ! 我本可以通過List<Human>
作為l
傳遞的,現在我的Human
列表中有一個Elephant
!
另一方面,你在哪里super
? super Foo
? super Foo
表示容器可以裝Foo
。
因此,對於void doIt2( List< ? extends Human> l ){}
,您可以傳入List<Human>
, List<Primate>
, List<Simian>
, List<Mammal>
或List<Object>
。
void ( List< ? super Human> l ){
l.add( new Human() );
}
很好,但是您不能保證會從容器中得到什么。
void ( List< ? super Human> l ){
Human h = l.get(0); //wrong!
}
這是因為,如果l
確實是List<Primate>
則它可能包含Human
和Gorilla
的混合,並且不能保證第一個元素將是Human
。
對於這一點,你會得到的縮寫PECS
= “[a] 芘 roducer **éxtends,[但]Çonsumer 小號 upers”。
如果您是容器正在將對象返回到某個方法(例如get
),那么您就是一個“生產者”(因此請使用extends
),否則,如果您正在接受容器中的元素(例如add
),則該容器正在“消費”對象,因此請使用“超級”。 (注:關於PECS,需要記住的棘手部分是它是從容器的角度而不是調用代碼的角度!)。
如果您想同時生產和消費,則需要有一個具體的類型列表List<Primate>
,您可以在其中添加Human
並檢索Primate
。
也可以看看:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.