[英]Why these generics don't compile in OpenJDK7, but do in OpenJDK6
class HasId<I> {}
class HasStringId extends HasId<String> {}
class Alert<T extends /*Some*/Object> extends HasStringId {}
class BaseController<M extends HasId<String>> {
// abstract Class<M> getModelClass();
}
class AlertController extends BaseController<Alert> { // error here
// @Override Class<Alert> getModelClass() {
// return Alert.class;
// }
}
在OpenJDK6上可以正常編譯,但在OpenJDK7中可以提供:
AlertController.java:50: error: type argument Alert is not within bounds of
type-variable T
class AlertController extends BaseController<Alert> {
^
where T is a type-variable:
T extends HasId<String> declared in class BaseController
請注意,在第50行有原始類型警告,因為必須將Alert參數化。 如果我這樣做,例如extends BaseController<Alert<Object>>
,代碼就會編譯。 但是我不能這樣做,因為我需要實現getModelClass()。
更新:這是Java 6實現中的錯誤,已在Java 7中修復: http : //bugs.sun.com/bugdatabase/view_bug.do?bug_id=6559182 。 (這是我對編譯器開發人員的問題: http : //openjdk.5641.n7.nabble.com/Nested-generics-don-t-compile-in-1-7-0-15-but-do-in-1 -6-0-27-td121820.html )
問題是HasId<String>
是否是原始類型Alert
的超類型。 規格在這個問題上不是很清楚。
本着 [4.8]的精神 ,原始類型的超類型也應全部刪除。 因此Alert
應該具有HasId
的超類型,但不能具有HasId<String>
。 但是,本節僅針對“超類/接口”進行討論,而不針對“超類型”進行討論。
本着[4.10]的精神,通過直接超型發現超型。 目前尚不清楚本節如何適用於原始類型。 它可能打算裁定原始Alert
具有直接超類型HasStringId
。 看起來很公平。 然后,由於HasId<String>
是HasId<String>
的直接超類型, HasStringId
通過傳遞性, HasId<String>
是Alert
的超類型!
混淆的根源在於實際上有兩種HasStringId
類型,一種是普通的,一種是原始的。 即使HasStringId
本身不是通用的,它也具有通用的超類型,因此談論HasStringId
的原始版本是HasStringId
。
規范沒有在普通HasStringId
和原始HasStringId
之間進行區分。 這是一個疏忽。
假設我們將原始的HasStringId
表示為HasStringId'
,那么[4.10]現在更有意義。 raw Alert
的直接超級接口是raw HasStringId'
。 Raw HasStringId'
的直接超級接口是raw HasId
。 因此, HasId
是Alert
的超類型,而不是HasId<String>
。
參見JLS的第4節 。 我在這里鏈接到上一個JLS,因為JLS 7在4.10.2節中存在嚴重的編輯錯誤
在許多通用的細微差別中, 有許多 Java 7編譯器的案例比Java 6編譯器更為嚴格。 這些情況通常與實際語言規范變得更加具體有關。 該錯誤可能與使用任何原始類型實質上在繼承的類型上“選擇退出”泛型有關—盡管是否正確尚有待商bat。
編輯:我在JDK 7不兼容列表中找不到此問題。 使用sun-jdk-1.7.0_10可以重現該錯誤,但使用Eclipse編譯器則無法重現(就泛型細微差別而言 ,Eclipse編譯器的記錄遠優於javac)。 您應該提交一個Oracle錯誤 。
這是一個可能的解決方法:
class AlertController extends BaseController<Alert<?>> {
@Override
@SuppressWarnings("unchecked")
Class<Alert<?>> getModelClass() {
return (Class<Alert<?>>)(Class<?>)Alert.class;
}
}
我認為這與在沒有實際類型參數的情況下如何處理擦除有關。 當引用沒有任何類型參數的參數化類型時,將刪除對這些參數的所有引用。
在這種情況下,將使用沒有任何類型參數的參數化類型Alert
。 這將刪除Alert
及其超類上的所有類型參數。 這將導致HasId
的擴展子句中的HasStringId
的類型參數被擦除。 然后Alert
不會將HasId<String>
子類HasId<String>
因為HasStringId
不再擴展它,而是擴展了HasId
。
Paul B.的變通方法或以下解決方法始終使用Alert
及其類型參數來避免此問題。
class AlertController<T> extends BaseController<Alert<T>> {
@Override Class<Alert<T>> getModelClass() {
return cast(Alert.class);
}
@SuppressWarnings("unchecked")
private <T> T cast(final Object o) {
return (T) o;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.