通配符在Java 7和8中的泛型行为不同

4

我有这段代码,当我将其编译为Java 7(Eclipse编译器)时,它可以正常编译,但是当我将项目设置为Java 8时,它会失败:

package scratch;

class Param<T extends Comparable<T>> {
  public Comparable<?> get() {
    return null;
  }
}

public class Condition<T extends Comparable<T>> {
  public static <T extends Comparable<T>> Condition<T> isInRange(T lower, T upper) {
    return null;
  }

  public void foo() {
    Comparable bound = null;                 // Line 15 
    Param<?> param = new Param<Double>();
    Condition.isInRange(param.get(), bound); // Line 17
  }
}

在Java 7中,我遇到以下警告:
  • 15行:Comparable是原始类型。应该将对泛型类型Comparable的引用参数化
  • 17行:类型安全性:调用通配符方法isInRange(T, T)时未经检查,其类型为Condition
当我在第15行添加<?>后,警告消失了,但是我遇到了第17行的错误:

边界不匹配:类型Condition的泛型方法isInRange(T, T)不适用于参数(Comparable, Comparable)。推断出的类型Comparable不是受限参数的有效替代

有人知道确切的原因是什么导致这种不兼容吗?
PS:我添加了这些丑陋的转换以使代码在Java的两个版本下都能编译成功。
    Condition.isInRange((Comparable)param.get(), (Comparable) bound);

2
Eclipse编译器在泛型方面存在一些错误,因此您应该使用javac进行测试。 - Kayaman
3
这与通配符无关,而是与bound变量的原始类型有关。在Java 7中,这实际上关闭了所有关于isInRange方法调用的检查。在Java 8中,目标类型仍会检测嵌套调用的无效性,类似于此场景。我不明白为什么您认为将类型转换为原始的Comparator比最初使用原始类型更难看。当然,由于bound已经是原始的Comparable,所以第二个类型转换没有意义。 - Holger
2
@Kayaman:在这种情况下,Eclipse 与 javac 完全一致。 - Holger
2
为什么要使用原始类型?除非你必须处理无法更改的旧(泛型之前的)代码,否则不要使用原始类型。 - Jesper
2
这个示例代码中没有可推导的解决方案。方法 isInRange 无法接受 Comparable<?>,因为该类型与所需参数类型不兼容。您可以通过使用原始类型来抑制泛型类型检查,但正确的解决方案是在第一次获取 Comparable<?> 时防止其出现。但我们无法从您的示例中推导出为什么会有这些不完整的类型... - Holger
显示剩余2条评论
1个回答

0
首先,我不是泛型方面的专家,所以我的回答可能不正确。我认为问题在于isInRange方法的定义方式。
<T extends Comparable<T>>

这是一个递归的定义,本身并不是问题。看看像 DoubleLong 这样的类。我相信由于递归定义的解析方式,方法(isInRange)的类型必须是实现了 Comparable 接口,并将自身作为 Comparable 的泛型;例如,class C implements Comparable<C>

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