我在升级为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> {
现在只允许将其扩展的字符串或类(由于字符串是最终的)。这样,您的后备类型将是字符串。