在Java8中,如果方法参数的转换未经检查,为什么返回类型的泛型会被删除?
考虑下面的代码示例:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List list = new ArrayList<Integer>();
String response = getProducer(list).get();
}
static Producer<String> getProducer(List<Integer> list) {
return new Producer<String>();
}
}
class Producer<T> {
T get() {
return null;
}
}
在Java 7中编译时,它只会为getProducer(list)
生成预期的警告:
Warning:(7, 39) java: unchecked conversion required:
java.util.List<java.lang.Integer>
found:java.util.List
但是,在Java 8中编译时,它会为response = getProducer(list).get()
赋值产生以下错误:
Error:(7, 48) java: incompatible types:
java.lang.Object
cannot be converted tojava.lang.String
显然,从getProducer(list)
返回的类型不是Producer<String>
,而是擦除的Producer
(这也通过IDE中的“提取变量”功能得到了确认)。这非常令人费解,因为getProducer
方法总是返回Producer<String>
奇怪的是,在调用getProducer
方法时,可以通过以下方式避免未经检查的转换来修复此问题:
- 将
getProducer
的参数类型从List<Integer>
更改为List
- 将
list
变量的类型从List
更改为List<Integer>
更新
- 使用的Java是Oracle JDK 1.8.040
- 我还尝试在Java8编译器中使用1.5到1.7的源代码和目标选项,结果是一样的李>
问题
- 当返回值的泛型类型在方法签名中固定时,传递的参数的泛型类型如何影响方法返回值的泛型类型李>
- 为什么Java 7和Java 8之间的行为会发生如此向后不兼容的变化李>
# 1 楼答案
这看起来像是here和here报告的已知兼容性问题
从第二个链接:
由此衍生出,OP的代码可以通过替换这一行来修复(右侧的类型声明让我感到不适——我将其解读为一个类型化数组列表,但它不是):
与
这不会导致从方法
getProducer(List<Integer> list)
的返回类型中删除类型再次引用第二链接:
# 2 楼答案
我猜原因是这样的,关注“迁移”兼容性-
如果调用为泛型类型的方法参数提供了原始类型参数,那么调用代码很可能是前泛型代码,并且用于前泛型的方法声明也已“进化”为使用泛型类型
为了保持完美的兼容性,傻瓜式的解决方案是删除方法类型,使其与泛型之前的版本一样。这样,调用的含义保持完全相同
例如,对于JLS来说,更复杂的解决方案可能太复杂了——如果存在原始类型参数,我们如何进行类型推断
如今,这种假设可能不再成立——调用更可能是后泛型代码,出于各种原因,它仍然使用原始类型。最好尽早“纠正”原始类型
# 3 楼答案
真正的问题是;为什么可以使用原始类型?为了向后兼容。如果是为了向后兼容,则假设只应使用原始类型
方法或构造函数有两种模式之一。它要么是完全泛型的,要么是完全原始类型的,以便向后兼容。没有一种使用模式是部分原始类型,部分通用的。选择原始类型的唯一原因是为了向后兼容,在这种情况下,它假设所有类型都是原始类型
由于Java 1.4不再受支持,而且已经有一段时间不受支持了,因此向后兼容性的论点不那么有力,在当前的语言中给原始类型一个位置是有意义的