Java泛型编译错误 - 类型<type>中的方法方法(类<capture#1-of?=“”extends =“”interface =“”>)不适用于参数</type> </capture# > 1-的

时间:2011-11-05 17:28:36

标签: java generics extends

上周四有人在工作时向我展示了一个编译错误,我无法以干净的方式解决这个问题,从那以后一直困扰着我。

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

我一直在寻找各种各样的互联网,但似乎找不到一个合适的解释为什么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的泛型类型是“Type”而不是“?extends Interface”。

4 个答案:

答案 0 :(得分:5)

一点建议:当您不确定为什么编译器禁止某些与泛型相关的转换时,请使用List<T>替换相关的泛型类。然后很容易找到打破类型安全的例子。

这种替换是正确的,因为目前Java没有提供一种方法来进行关于泛型类的可能行为的任何小修知识(即它缺乏在声明中指定泛型类的协方差和逆变的方法,如在C#4中和斯卡拉)。因此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!

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

你会同意这不应该编译:

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

问题是javac有点愚蠢;在编译第2行时,它所知道的变量clazz是它的声明类型;它会忘记它所分配的具体类型。因此,在第1行分配给clazz并不重要,编译器必须拒绝第2行。

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

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

答案 3 :(得分:0)

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

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

与这个问题没有关系,但我建议谨慎使用反思。这是一个很好的解决方案。