Java 8类型推理导致省略调用时的通用类型



我在升级为Java 1.8后使用通用方法有问题,Java 1.6和1.7很好考虑以下代码:

public class ExtraSortList<E> extends ArrayList<E> {
    ExtraSortList(E... elements) {
        super(Arrays.asList(elements));
    }
    public List<E> sortedCopy(Comparator<? super E> c) {
        List<E> sorted = new ArrayList<E>(this);
        Collections.sort(sorted, c);
        return sorted;
    }
    public static void main(String[] args) {
        ExtraSortList<String> stringList = new ExtraSortList<>("foo", "bar");
        Comparator<? super String> compGen = null;
        String firstGen = stringList.sortedCopy(compGen).get(0); // works fine
        Comparator compRaw = null;
        String firstRaw = stringList.sortedCopy(compRaw).get(0); // compiler ERROR: Type mismatch: cannot convert from Object to String
    }
}

我使用Oracle Javac(1.8.0_92)和Eclipse JDT(4.6.1)编译器尝试了此操作。这两者都是相同的结果。(错误消息有些不同,但本质上是相同的)

在事实之外,可以通过避免原始类型来防止错误,因为我不了解原因。

为什么sortedCopy方法的原始方法参数对返回值的通用类型有任何影响?通用类型已经在类级别定义。该方法不能定义单独的通用类型。参考list键入<String>,因此返回的列表也应。

为什么java 8在返回值上从类中丢弃一类的通用类型?

编辑:如果更改了SortedCopy的方法签名(如Biziclop指出)为

public List<E> sortedCopy(Comparator c) {

然后,编译器确实考虑了ExtraSortList<E>类型的通用类型E,并且出现了任何错误。但是现在参数c是一种原始类型,因此编译器无法验证提供的比较器的通用类型。

编辑:我对Java语言规范进行了一些评论,现在我想到了,无论我缺乏理解还是这是编译器中的缺陷。因为:

  • 通用类型E声明的范围是类ExtraSortList,其中包括方法sortedCopy
  • 方法sortedCopy本身确实不声明一个通用类型变量,它只是指类范围范围的类型变量E。请参阅JLS中的通用方法
  • JLS也在同一部分中指出
    当调用通用方法时,可能不需要明确提供类型参数,因为通常可以推断出它们(§18(类型推理))。
  • 参考stringList是用String定义的,因此编译器不需要在sortedCopy的调用中推断E的类型,因为它已经定义。
  • 因为stringList已经具有用于E的REFIFIED类型,因此给定调用的参数c应该为Comparator<? super String>
  • 返回类型还应使用已修复的类型E,因此应为List<String>

这是我目前对我认为Java编译器如何评估调用的理解。如果我错了,一个解释为什么我的假设错误会很好。

为何发生这种情况的最终答案:

就像@jesper已经提到的那样,当您不应该时,您正在使用原始类型(尤其是在多种情况下使用通用作为类型时)。由于您通过没有通用类型的比较器,实际上将没有。您可以将电子生物视为无效,以使其更容易。因此,您的代码成为此:

public List sortedCopy(Comparator c) {
    List sorted = new ArrayList(this);
    Collections.sort(sorted, c);
    return sorted;
}

现在,您正在尝试/假设您从列表中获得一个没有仿制药的字符串,因此是一个对象(因此,这是所有事物的超级级别)。

对于为什么原型参数对返回类型没有影响的问题,因为您没有指定一定的抽象水平。例如

public class ExtraSortList<E extends String> extends ArrayList<E> {

现在只允许将其扩展的字符串或类(由于字符串是最终的)。这样,您的后备类型将是字符串。

最新更新