簡體   English   中英

Java Generics Hell

[英]Java Generics Hell

我懷疑這里曾經問過(並回答過),但我不知道如何命名問題。 為什么只有在我沒有通過課程本身時才能毫無問題地表達通配符?

這一切都歸結為這段代碼。 除了調用genericsHell(ShapeSaver.class)之外,一切都按預期工作:

interface Shape { }

interface Circle extends Shape { }

interface ShapeProcessor<T extends Shape> { }

class CircleDrawer implements ShapeProcessor<Circle> { } 

class ShapeSaver<T extends Shape> implements ShapeProcessor<T> { }

class Test {
    void genericsHeaven(ShapeProcessor<? extends Shape> a) {}

    void genericsHell(Class<? extends ShapeProcessor<? extends Shape>> a) {}

    void test() {
        genericsHeaven(new CircleDrawer());
        genericsHeaven(new ShapeSaver<Circle>());
        genericsHell(CircleDrawer.class);
        genericsHell(ShapeSaver.class); // ERROR: The method genericsHell is not applicable for the arguments (Class<ShapeSaver>)
    }
}

ShapeSaver.class的類型是Class<ShapeSaver> 將它提供給genericsHell() ,編譯器需要檢查Class<ShapeSaver>是否是Class<? extends ShapeProcessor<?>的子類型Class<? extends ShapeProcessor<?> Class<? extends ShapeProcessor<?> ,減少ShapeSaver是否是ShapeProcessor<?>的子類型。 子類型關系不成立,方法調用失敗。

同樣的事情應該適用於@ Bohemian的解決方案。 這里,在推斷出T之后,在T綁定檢查時發生子類型檢查。 它也應該失敗。 這似乎是一個編譯器錯誤,它以某種方式錯誤解釋了Raw可分配給Raw<X>的規則,就像RawRaw<X>的子類型一樣。 另請參閱Enum.valueOf會針對擴展Enum的未知類型類型發出警告嗎?

解決問題的一個簡單方法是聲明

void genericsHell(Class<? extends ShapeProcessor> a)

的確, ShapeSaver是一個亞型ShapeProcessor ,呼叫編譯。

這不僅僅是一種解決方法。 這是有充分理由的。 嚴格來說,對於任何Class<X>X必須是原始類型。 例如, Class<List>是好的, Class<List<String>>不是。 因為實際上沒有代表List<string> ; 只有一個代表List的類。

忽略嚴厲的警告,你不應該使用原始類型。 考慮到Java類型系統的設計方式,我們有時必須使用原始類型。 甚至Java的核心API( Object.getClass() )也使用原始類型。


你可能打算做這樣的事情

genericsHell(ShapeSaver<Circle>.class);

不幸的是,這是不允許的。 Java可能有,但沒有,引入類型文字和泛型。 這給很多庫帶來了很多問題。 java.lang.reflect.Type是亂七八糟的,無法使用。 每個庫都必須引入自己的類型系統表示來解決問題。

你可以借一個,例如來自Guice,你就可以

genericsHell( new TypeLiteral< ShapeSaver<Circle> >(){} )
                               ------------------  

(學習在閱讀代碼時跳過ShaveSaver<Circle>周圍的擲骰子)

genericsHell()的方法體中,您將擁有完整的類型信息,而不僅僅是類。

鍵入genericsHell方法允許它編譯:

static <T extends ShapeProcessor<?>> void genericsHell(Class<T> a) {}

編輯:這允許編譯器從上下文或通過編碼顯式類型指定ShapeProcessor不是字面上的任何 ShapeProcessor ,而是作為參數傳遞的類型相同的類型。 如果調用是顯式類型的,(編譯器在封面下執行),代碼將如下所示:

MyClass.<ShapeSaver>genericsHell(ShapeSaver.class);

有趣的是,它提供了類型警告 ,但仍然編譯。 但是,不需要顯式類型,因為從參數中可以獲得足夠的類型信息來推斷泛型類型。


你的問題遺漏了一些聲明,所以我添加了它們以創建一個簡短的自包含正確的示例 - 即此代碼按原樣編譯

static interface Shape { }

static interface Circle extends Shape { }

static interface ShapeProcessor<T extends Shape> { }

static class CircleDrawer implements ShapeProcessor<Circle> { }

static class ShapeSaver<T extends Shape> implements ShapeProcessor<T> { }

static void genericsHeaven(ShapeProcessor<? extends Shape> a) { }

// The change was made to this method signature:
static <T extends ShapeProcessor<?>> void genericsHell(Class<T> a) { }

static void test() {
    genericsHeaven(new CircleDrawer());
    genericsHeaven(new ShapeSaver<Circle>());
    genericsHell(CircleDrawer.class);
    genericsHell(ShapeSaver.class);
}

暫無
暫無

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

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