为什么在Java 8中有一个未经检查的方法参数转换时,返回类型的泛型会被擦除? [英] Why is generic of a return type erased when there is an unchecked conversion of a method parameter in Java 8?
问题描述
pre $ 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;
$ b当使用Java 7编译时,它只会产生一个期望的警告:(7,39)警告:
getProducer(list)
: $ b
:unchecked conversion
required:java.util.List< java.lang.Integer>
找到:java.util。然而,当在Java 8中编译时,它会为<$ c $产生以下错误:
c> response = getProducer(list).get()赋值:
lockquote
错误:(7,48) java:incompatible types: java.lang.Object
不能转换为 java.lang.String
$ p
$ b
显然,从 getProducer(list)
返回的类型不是 Producer< ; String>
,但擦除 Producer
(这也可以通过IDE中的提取变量功能进行确认)。这非常令人费解,因为 getProducer
方法总是返回 Producer< String>
。
奇怪的是,它可以通过在调用 getProducer
方法时通过避免未经检查的转换来修复,可以通过以下方式:
getProducer
从 List< Integer>
更改为 List
list 变量的类型
List
至列表<整数>
更新
问题
<$ li>
从第二个链接:
,在JDK 7中不会在JDK 8中进行
编译:
import java。 util.List;
class SampleClass {
static class Baz< T> {
public static List< Baz< Object>> sampleMethod(Baz< Object> param){
return null;
private static void bar(Baz arg){
Baz element = Baz.sampleMethod(arg).get(0);
$ block $
编译这段代码JDK 8产生以下错误:
SampleClass.java:12:错误:不兼容的类型:对象不能被
转换成Baz
Baz element = Baz.sampleMethod(arg).get(0);
注意:SampleClass.java使用未经检查或不安全的操作。注意:
使用-Xlint重新编译:取消选中以获取详细信息。 1错误
由此得出,OP的代码可以通过替换这一行来修复(右边的类型声明使我关闭 - 我读它是一个类型化的数组列表,它不是):
List list = new ArrayList< Integer>() ;
with
列表与LT;整数> list = new ArrayList< Integer>();
这不会导致类型被方法的返回类型擦除 getProducer (列表<整数>列表)
再次引用第二个链接:
在这个例子中,原始类型被传递给
sampleMethod(Baz< Object>)
请参阅
JLS,Java SE 7 Edition,第15.12.2.2节)。未检查的
转换对于该方法的适用是必要的,因此它的返回
类型被擦除(请参阅JLS,Java SE 7 Edition,第15.12.2.6节)。在
这种情况下,返回类型 sampleMethod(Baz< Object>)
是
java.util.List
而不是 java.util.List< Baz< Object>>
,因此
返回类型 get(int )
是 Object
,它与 Baz
不是赋值兼容的
。
Consider the following code sample:
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;
}
}
When compiled in Java 7 it just produces an expected warning for getProducer(list)
:
Warning:(7, 39) java: unchecked conversion
required: java.util.List<java.lang.Integer>
found: java.util.List
However, when compiled in Java 8 it produces the following error for the response = getProducer(list).get()
assignment:
Error:(7, 48) java: incompatible types: java.lang.Object
cannot be converted to java.lang.String
So apparently the type returned from getProducer(list)
is not Producer<String>
, but erased Producer
(which is also confirmed by means of the 'extract variable' feature in the IDE). This is very puzzling because getProducer
method always returns Producer<String>
.
Oddly enough it could be fixed by avoiding unchecked conversion while calling getProducer
method, either by:
- Change parameter type of
getProducer
from List<Integer>
to List
- Change type of
list
variable from List
to List<Integer>
Updates
- Java used is Oracle JDK 1.8.0_40
- I have also tried using source and target options from 1.5 through 1.7 with the Java 8 compiler and the result was the same.
Questions
- How could the generic type of the passed argument affect a generic type of the method return value while the generic type of the return value is fixed in the method signature?
- Why is there such a backward-incompatible change in behavior between Java 7 and Java 8?
解决方案 This looks like a known compatibility issue reported here and here.
From the second link:
The following code which compiled, with warnings, in JDK 7 will not
compile in JDK 8:
import java.util.List;
class SampleClass {
static class Baz<T> {
public static List<Baz<Object>> sampleMethod(Baz<Object> param) {
return null;
}
}
private static void bar(Baz arg) {
Baz element = Baz.sampleMethod(arg).get(0);
}
}
Compiling this code in JDK 8 produces the following error:
SampleClass.java:12: error:incompatible types: Object cannot be
converted to Baz
Baz element = Baz.sampleMethod(arg).get(0);
Note: SampleClass.java uses unchecked or unsafe operations. Note:
Recompile with -Xlint:unchecked for details. 1 error
Deriving from this, the OP's code can be fixed by replacing this line (the type declartion on the right hand side threw me off - I read it as a typed array list which it is not):
List list = new ArrayList<Integer>();
with
List<Integer> list = new ArrayList<Integer>();
which will not result in type being being erased from return type of method getProducer(List<Integer> list)
Quote from second link again:
In this example, a raw type is being passed to the
sampleMethod(Baz<Object>)
method which is applicable by subtyping (see
the JLS, Java SE 7 Edition, section 15.12.2.2). An unchecked
conversion is necessary for the method to be applicable, so its return
type is erased (see the JLS, Java SE 7 Edition, section 15.12.2.6). In
this case the return type of sampleMethod(Baz<Object>)
is
java.util.List
instead of java.util.List<Baz<Object>>
and thus the
return type of get(int)
is Object
, which is not assignment-compatible
with Baz
.
这篇关于为什么在Java 8中有一个未经检查的方法参数转换时,返回类型的泛型会被擦除?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!