下面的代码片段:
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