[英]Java 8 type inference cause to omit generic type on invocation
I have a Problem with a generic method after upgrading to Java 1.8, which was fine with Java 1.6 and 1.7 Consider the following code: 升级到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
}
}
I tried this with the Oracle javac (1.8.0_92) and with Eclipse JDT (4.6.1) compiler. 我尝试使用Oracle javac(1.8.0_92)和Eclipse JDT(4.6.1)编译器。 It is the same result for both.
两者都是一样的结果。 (the error message is a bit different, but essentially the same)
(错误消息有点不同,但基本相同)
Beside the fact, that it is possible to prevent the error by avoiding raw types, it puzzles me, because i don't understand the reason. 除了事实,可以通过避免原始类型来防止错误,它让我困惑,因为我不明白原因。
Why does the raw method parameter of the sortedCopy-Method have any effect on the generic type of the return value? 为什么sortedCopy-Method的raw方法参数对返回值的泛型类型有任何影响? The generic type is already defined at class level.
泛型类型已在类级别定义。 The method does not define a seperate generic type.
该方法未定义单独的泛型类型。 The reference
list
is typed to <String>
, so should the returned List. 引用
list
键入<String>
,返回的List也应该键入。
Why does Java 8 discard the generic type from the class on the return value? 为什么Java 8会在返回值上丢弃类中的泛型类型?
EDIT: If the method signature of sortedCopy is changed (as pointed out by biziclop) to 编辑:如果sortedCopy的方法签名被更改(由biziclop指出)
public List<E> sortedCopy(Comparator c) {
then the compiler does consider the generic type E
from the type ExtraSortList<E>
and no error appears. 然后编译器会从
ExtraSortList<E>
类型中考虑泛型类型E
,并且不会出现错误。 But now the parameter c
is a raw type and thus the compiler cannot validate the generic type of the provided Comparator. 但是现在参数
c
是原始类型,因此编译器无法验证提供的Comparator的泛型类型。
EDIT: I did some review of the Java Language Specification and now i think about, whether i have a lack of understanding or this is a flaw in the compiler. 编辑:我做了一些Java语言规范的评论,现在我想一想,我是否缺乏理解,或者这是编译器的一个缺陷。 Because:
因为:
E
is the class ExtraSortList
, this includes the method sortedCopy
. E
的声明范围是ExtraSortList
类,其中包括sortedCopy
方法。 sortedCopy
itself does not declare a generic type variable, it just refers to the type variable E
from the class scope. sortedCopy
本身不声明泛型类型变量,它只是从类作用域引用类型变量E
see Generic Methods in the 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)).
调用泛型方法时,可能不需要显式提供类型参数,因为它们通常可以被推断出来(§18(类型推断))。
stringList
is defined with String
, thus the compiler does not need to infer a type for E
on the invocation of sortedCopy
because it is already defined. stringList
是使用String
定义的,因此编译器不需要在调用sortedCopy
推断E
的类型,因为它已经定义。 stringList
already has a reified type for E
, the parameter c
should be Comparator<? super String>
stringList
已经具有E
的具体类型,所以参数c
应该是Comparator<? super String>
Comparator<? super String>
for the given invocation. Comparator<? super String>
。 E
, thus it should be List<String>
. E
,因此它应该是List<String>
。 This is my current understanding of how i think the Java compiler should evaluate the invocation. 这是我目前对我认为Java编译器应如何评估调用的理解。 If i am wrong, an explanation why my assumptions are wrong would be nice.
如果我错了,解释为什么我的假设是错误的将是很好的。
To bring an final answer to why this happens: 为了解决这个问题的最终答案:
Like @Jesper mentioned already, you're using raw types when you shouldn't (Especially when using the Generic as type in multiple cases). 就像@Jesper已经提到的那样,你不应该使用原始类型(特别是在多种情况下使用Generic作为类型)。 Since you pass an Comparator without an Generic-Type, there will actually be none.
由于您传递的是没有Generic-Type的Comparator,实际上没有。 You could think of the E-Generic as null to make it easier.
您可以将E-Generic视为null以使其更容易。 Therefore your code becomes to this:
因此,您的代码变为:
public List sortedCopy(Comparator c) {
List sorted = new ArrayList(this);
Collections.sort(sorted, c);
return sorted;
}
Now you're attemptig/assuming you get an String from an List without Generics and therefore an Object (hence it's the super-class of everything ). 现在你正在尝试/假设你从没有泛型的List中得到一个String,因此得到一个Object(因此它是所有东西的超类)。
To the question why the raw-type parameter has no effect on the return type, since you don't specify an certain Level of abstraction. 关于为什么raw-type参数对返回类型没有影响的问题,因为你没有指定某个抽象级别。 You'd have to define an Type that the Generic has to extend/implement at least to make that happen (compilation errors), for example.
例如,您必须定义Generic必须扩展/实现的类型,以至少实现这一点(编译错误)。
public class ExtraSortList<E extends String> extends ArrayList<E> {
will now only allow Strings or Classes which extend it (not possible here since string is final). 现在只允许扩展它的字符串或类(这里不可能,因为字符串是最终的)。 With that, your fallback Type would be String.
有了它,你的后备类型将是String。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.