繁体   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