Java有界泛型:类型推断bug?(方法调用,JLS 15.12.2.7)



下面的代码片段:

import java.util.List;
public class Main {
    interface Interface1<T> {}
    interface Interface2<T> extends Interface1<T> {}
    static class Bound {}
    interface BoundedI1<T extends Bound> extends Interface1<T> {}
    interface BoundedI2<T extends Bound> extends Interface2<T> {}
    public static void main(String[] args) {
        test((List<BoundedI2<?>>) null);
        //test2((List<BoundedI2<?>>) null);
    }
    public static void test(List<? extends Interface2<? extends Bound>> list) { test2(list); }
    public static void test2(List<? extends Interface1<? extends Bound>> list) {}
}

编译器对第一个调用没有问题,但是如果我取消第二个调用的注释就会抱怨。这是类型推理系统中的一个bug吗?或者有人能解释一下为什么JLS中的推理规则在这里失败了吗?

在6u43和7u45 Oracle jdk上测试

更新:似乎eclipsec接受它很好。不幸的是,我不能真正改变我们的工具链:P,但是发现编译器之间的差异是很有趣的。

ideone(很酷的工具)打印的错误信息:

Main.java:12: error: method test2 in class Main cannot be applied to given types;
        test2((List<BoundedI2<?>>) null);
        ^
  required: List<? extends Interface1<? extends Bound>>
  found: List<BoundedI2<?>>
  reason: actual argument List<BoundedI2<?>> cannot be converted to List<? extends Interface1<? extends Bound>> by method invocation conversion

UPDATE 2: This compilesfine,这表明编译器确实认为BoundedI2<?>是可分配给Interface1<? extends Bound>的,这似乎更直接地与JLS相矛盾:

public class Main {
    interface Interface1<T> {}
    interface Interface2<T> extends Interface1<T> {}
    static class Bound {}
    interface BoundedI1<T extends Bound> extends Interface1<T> {}
    interface BoundedI2<T extends Bound> extends Interface2<T> {}
    public static void main(String[] args) {
        test((List<BoundedI2<?>>) null);
        //test2((List<BoundedI2<?>>) null);
        test3((BoundedI2<?>) null);
    }
    public static void test(List<? extends Interface2<? extends Bound>> list) { test2(list); }
    public static void test2(List<? extends Interface1<? extends Bound>> list) {}
    public static void test3(Interface1<? extends Bound> instance) {}
}

看起来命令行编译器同时有一些处理困难

  • BoundedI2在T上是通用的,它必须是"Bound"
  • Interface2正在扩展Interface1

至少没有正确实例化BoundedI2。真正奇怪的是,在同一个JDK上配置的Eclipse可以很好地编译它……请注意,Eclipse使用它的内部编译器,以便在您键入时处理增量重新计算,因此它根本不会调用JDK的编译器(参见org/eclipse/jdt/internal/compiler package)。

这个修改使得它在Eclipse和命令行中都可以正常编译,通过强制BoundedI2基于具体类型而不是类型推断:

import java.util.List;
public class PerfTest {
    interface Interface1<T> {}
    interface Interface2<T> extends Interface1<T> {}
    static class Bound {}
    interface BoundedI1<T extends Bound> extends Interface1<T> {}
    interface BoundedI2<T extends Bound> extends Interface2<T> {}
    static class Actual extends Bound {}
    public static void main(String[] args) {
        test((List<BoundedI2<Actual>>) null);
        test2((List<BoundedI2<Actual>>) null);
    }
    public static void test(List<? extends Interface2<? extends Bound>> list) { test2(list); }
    public static void test2(List<? extends Interface1<? extends Bound>> list) {}
}

目前正在跟踪OpenJDK的错误报告,并提供一些见解:https://bugs.openjdk.java.net/browse/JDK-8051807

最新更新