簡體   English   中英

Java泛型怪異行為

[英]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);

基本上,您需要確定更重要的是什么,要對要放入包裝器中的內容,包裝內容或包裝器中取出的內容進行泛型(您不能安全地兼顧兩者)。 確定后,您將需要在extendssuper之間進行選擇。

如果只想存儲指針,則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>則它可能包含HumanGorilla的混合,並且不能保證第一個元素將是Human

對於這一點,你會得到的縮寫PECS = “[a] roducer **éxtends,[]Çonsumer 小號 upers”。

如果您是容器正在將對象返回到某個方法(例如get ),那么您就是一個“生產者”(因此請使用extends ),否則,如果您正在接受容器中的元素(例如add ),則該容器正在“消費”對象,因此請使用“超級”。 (注關於PECS,需要記住的棘手部分是它是從容器的角度而不是調用代碼的角度!)。

如果您想同時生產和消費,則需要有一個具體的類型列表List<Primate> ,您可以在其中添加Human並檢索Primate


也可以看看:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM