升级到
Java 1.8后,我遇到了一个泛型方法的问题,这对于Java 1.6和1.7来说很好
请考虑以下代码:
public class ExtraSortList extends ArrayList {
ExtraSortList(E... elements) {
super(Arrays.asList(elements));
}
public List sortedCopy(Comparator super E> c) {
List sorted = new ArrayList(this);
Collections.sort(sorted, c);
return sorted;
}
public static void main(String[] args) {
ExtraSortList 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-Method的raw方法参数对返回值的泛型类型有任何影响?泛型类型已在类级别定义.该方法未定义单独的泛型类型.引用列表的类型为< String>,返回的List也应如此.
为什么Java 8会在返回值上丢弃类中的泛型类型?
编辑:如果sortedCopy的方法签名被更改(由biziclop指出)
public List sortedCopy(Comparator c) {
那么编译器确实从类型ExtraSortList< E>中考虑通用类型E.并且不会出现错误.但是现在参数c是原始类型,因此编译器无法验证提供的Comparator的泛型类型.
编辑:我做了一些Java语言规范的评论,现在我想一想,我是否缺乏理解,或者这是编译器的一个缺陷.因为:
> Scope of a Declaration的泛型类型E是ExtraSortList类,它包括方法sortedCopy.
>方法sortedCopy本身不声明泛型类型变量,它只是从类范围引用类型变量E.见JLS中的Generic Methods
> JLS也在同一部分中说明
Type arguments may not need to be provided explicitly when a generic method is invoked, as they can often be inferred (§18 (Type Inference)).
>引用stringList是使用String定义的,因此编译器不需要在sortedCopy的调用中推断类型forE,因为它已经定义.
>因为stringList已经具有E的具体类型,所以参数c应该是Comparator对于给定的调用.
>返回类型也应该使用已经具体化的类型E,因此它应该是List< String>.
这是我目前对我认为Java编译器应如何评估调用的理解.如果我错了,解释为什么我的假设是错误的将是很好的.