为什么在Java 8中有一个未经检查的方法参数转换时,返回类型的泛型会被擦除? [英] Why is generic of a return type erased when there is an unchecked conversion of a method parameter in Java 8?

查看:737
本文介绍了为什么在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 列表<整数>



更新




  • Java使用的是Oracle JDK 1.8.0_40

  • 我也尝试使用从1.5到1.7的源和目标选项, Java 8编译器和结果是一样的。



问题




  • 传递的参数的泛型类型如何影响泛型类型的方法返回值,而返回值的泛型类型是fi为什么在Java 7和Java 8之间的行为中存在这种向后不兼容的变化?
    <$ 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆