[英]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>
的規則,就像Raw
是Raw<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.