簡體   English   中英

為什么這些泛型不在OpenJDK7中編譯,而在OpenJDK6中編譯

[英]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 因此, HasIdAlert的超類型,而不是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.

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