簡體   English   中英

Java泛型編譯錯誤-方法method(Class <capture#1-of ? extends Interface> ) <type> 不適用於參數

[英]Java generics compilation error - The method method(Class<capture#1-of ? extends Interface>) in the type <type> is not applicable for the arguments

上周四,有人在工作給我看了一個編譯錯誤,我無法以一種干凈的方式解決它,此后一直困擾着我。

問題是與泛型有關,我重構了生成編譯錯誤的簡化代碼。 該錯誤發生在下面顯示的最后一行代碼中。

我一直在遍及整個Interweb,但是似乎找不到關於Java編譯器不接受代碼的正當解釋。 我猜想如果允許代碼,有可能在Bar.operationOnBar()中創建一個類強制轉換問題,但我不知道如何。

有人可以啟發我為什么不編譯嗎?

public interface Interface {
}


public class Type implements Interface {
}

public class Bar<T> {
    public Bar(Class<T> clazz) {
    }

    public void operationOnBar(Class<T> arg){
    }
}

public class Foo {
    public <T> Bar<T> bar(Class<T> clazz){
        return new Bar<T>(clazz);
    }
    public static void main(String[] args) {
        Class<? extends Interface> extendsInterfaceClazz = Type.class;
        new Foo().bar(extendsInterfaceClazz).operationOnBar(Type.class);
    }
}

Foo.main()第二行的編譯錯誤:

The method operationOnBar(Class<capture#1-of ? extends Interface>) in the type Bar<capture#1-of ? extends Interface> is not applicable for the arguments (Class<Type>)

順便說一句。 我已經通過將Type.class下放到Class中來解決它,這樣編譯器無法看到Class的通用類型是“ Type”而不是“?extended Interface”。

一些建議:當不確定為什么編譯器禁止某些與通用相關的轉換時,請使用List<T>替換相關的通用類。 然后,很容易找到破壞類型安全性的示例。

這種替換是正確的,因為當前Java沒有提供一種方法來進行有關泛型類可能行為的先驗知識(即,它缺乏一種在其聲明中指定泛型類的協方差和相反性的方法,例如C#4和Scala)。 。 因此, Class<T>List<T>就其可能的行為而言,對於編譯器是等效的,並且編譯器必須禁止對其他通用類也可能導致List<T>問題的轉換。

在您的情況下:

public class Bar<T> {
    private List<T> l;

    public Bar(List<T> l) {
        this.l = l;
    }

    public void operationOnBar(List<T> arg) {
        l.addAll(arg);
    }
}

List<Type1> l1 = new ArrayList<Type1>();
List<? extends Interface> l2 = l1;
List<Type2> l3 = Arrays.asList(new Type2());

new Foo().bar(l2).operationOnBar(l3);

Type1 t = l1.get(0); // Oops!

您也可以將方法operationOnBar的簽名更改為:

public void operationOnBar(Class<? extends Interface> arg){

您將同意不應編譯:

 1   Class<? extends Interface> clazz = AnotherType.class;
 2   new Foo().bar(clazz).operationOnBar(Type.class);

問題是javac有點愚蠢。 在編譯第2行時,它對變量clazz所了解的只是其聲明的類型。 它忘記了分配給它的具體類型。 因此,在第1行分配給clazz內容無關緊要,編譯器必須拒絕第2行。

我們可以想象一個更聰明的編譯器可以跟蹤具體類型,然后可以編譯您的代碼,因為它顯然是安全且正確的。

由於不是這種情況,所以有時程序員比編譯器更了解類型,所以有必要讓程序員進行強制轉換以說服編譯器。

處理此類問題的一般方法是為重復類型引入通用參數,這通常意味着引入新的通用方法(一個類也可以,但不是必需的)。

public static void main(String[] args) {
    fn(Type.class);
}
private static <T extends Interface> void fn(Class<T> extendsInterfaceClazz) {
    new Foo().bar(extendsInterfaceClazz).operationOnBar(extendsInterfaceClazz);
}

並非與問題真正相關,但我建議您謹慎使用反射。 這是一個非常非常好的解決方案。

暫無
暫無

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

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