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

10
以下是代码片段:

对于以下的代码块:

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中的推断规则在这里失败了?
在Oracle JDK 6u43和7u45上进行了测试。
更新:似乎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

更新2:这个编译没有问题,这表明编译器认为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) {}
}

2
我使用Java 1.6.0_26-b03将你的代码粘贴到Eclipse中,编译没有任何问题。你遇到了什么错误? - azurefrog
2
虽然Eclipse对该代码没有任何问题,但是当我尝试使用命令行中的javac编译它时,我确实遇到了一个错误:test2 (java.util.List<? extends Main.Interface1<? extends Main.Bound>>) in Main cannot be applied to (java.util.List<Main.BoundedI2<?>>)。我已经双重检查了Eclipse使用的路径,以确保它是相同的编译器。这非常有趣。 - azurefrog
这里没问题。已在Eclipse + JDK1.7.0_45上进行了测试。 - Wundwin Born
这个也在JDK 1.8.0_05下无法编译,所以Java 8中改进的类型推断规则没有修复这个问题...有趣的是,将注释掉的行中的<?>替换为<? extends Bound>不会引发错误。可能推断规则不考虑多个推断类型的"组合",但我对规则不够熟悉,无法确定是否如此。另外一个问题是,我不确定<?>应该正确地被视为<? extends Object>还是<? extends Bound>,但那是另一个问题... - awksp
我在这里感到困惑。 test()不应该编译,因为List <BoundedI2 <?>>不能赋值给List <? extends Interface2 <? extends Bound>>吗?具体来说,BoundedI2扩展了Interface2,但是<?>不是<? extends Bound>。我错过了什么吗?如果定义BoundedI2<T extends Bound>,那么编译器是否足够聪明,知道<?>应该是<? extends Bound>?这甚至是一个有效的主张吗? - awksp
显示剩余6条评论
2个回答

1
似乎命令行编译器在同时处理以下内容时存在一些困难:
- BoundedI2是基于必须为“Bound”的T的泛型事实。 - Interface2扩展了Interface1的事实。
至少没有正确实例化BoundedI2。确实奇怪的是,配置在同一JDK上的Eclipse可以正常编译它...请注意,Eclipse使用其内部编译器来处理增量重新编译,因此根本不调用JDK的编译器(请参见org /eclipse/jdt/internal/compiler包)。
通过将BoundedI2强制为具体类型,而不是类型推断,这种修改使得它在Eclipse和命令行中都能够正常编译。
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) {}
}

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接