【问题标题】:Java generics compilation error - The method method(Class<capture#1-of ? extends Interface>) in the type <type> is not applicable for the argumentsJava 泛型编译错误 - <type> 类型中的方法 method(Class<capture#1-of ? extends Interface>) 不适用于参数
【发布时间】:2011-12-22 17:48:18
【问题描述】:

上周四,工作人员向我展示了一个编译错误,我无法以干净的方式修复它,此后一直困扰着我。

问题与泛型有关,我重构了生成编译错误的代码的简化版本。错误发生在下面显示的最后一行代码中。

我一直在寻找整个互联网,但似乎找不到一个体面的解释为什么 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”而不是“? extends Interface”。

【问题讨论】:

    标签: java generics extends


    【解决方案1】:

    处理这类问题的一般方法是为重复类型引入一个泛型参数,这通常意味着引入一个新的泛型方法(类也可以,但不是必需的)。

    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);
    }
    

    与问题无关,但我建议谨慎使用反射。这很少是一个好的解决方案。

    【讨论】:

      【解决方案2】:

      你会同意这不应该编译:

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

      问题是javac有点笨;在编译第 2 行时,它所知道的变量clazz 就是它声明的类型;它忘记了分配给它的具体类型。所以在第 1 行分配给 clazz 的内容无关紧要,编译器必须拒绝第 2 行。

      我们可以想象一个更智能的编译器,它可以跟踪具体类型,然后你的代码就可以被编译,因为它显然是安全和正确的。

      由于情况并非如此,有时程序员比编译器更了解类型,因此程序员有必要进行强制转换以说服编译器。

      【讨论】:

        【解决方案3】:

        一个小建议:当您不确定编译器为何禁止某些与泛型相关的转换时,请将有问题的泛型类替换为List&lt;T&gt;。那么很容易找到一个破坏类型安全的例子。

        这种替换是正确的,因为目前 Java 没有提供一种方法来对泛型类的可能行为进行任何先验知识(即它缺乏一种在泛型类的声明中指定协变和逆变的方法,如在 C# 4 中一样和斯卡拉)。因此,Class&lt;T&gt;List&lt;T&gt; 在编译器可能的行为方面是等效的,并且编译器必须禁止可能导致 List&lt;T&gt; 出现问题的其他泛型类的转换。

        在你的情况下:

        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!
        

        【讨论】:

          【解决方案4】:

          您也可以将operationOnBar方法的签名更改为:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-04-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-08-29
            • 2015-06-14
            • 2014-01-29
            相关资源
            最近更新 更多