【问题标题】:How does Java type erasure work when the type parameter is used for casting?当类型参数用于强制转换时,Java 类型擦除如何工作?
【发布时间】:2019-09-19 03:56:15
【问题描述】:

我知道通常未绑定的类型参数在编译时会替换为Object。但是这段代码是如何工作的呢?

<T> void call(List<T> list, Object o) {
    fun((T) o);
}

会编译成

void call(List list, Object o) {
    fun((Object) o);
}

这似乎是一个错误的情况,因为o 应该转换为与列表中的元素相同的类型?

【问题讨论】:

    标签: java generics type-erasure


    【解决方案1】:

    由于类型擦除,即使您的类型是A,在您的示例中它也会被视为Object,正如您所猜测的那样。这意味着如果您传递了除A 之外的第二个元素,它实际上将被转换为Object

    例子,

    import java.util.ArrayList;
    import java.util.List;
    
    public class TypeErasure {
    
        static <A> void call(List<A> list, Object elem) {
            A o1 = (A) elem;
            System.out.println(o1);
        }
    
        static void callV2(List list, Object elem) {
            System.out.println(elem);
        }
    
        //bounded type
        static <A extends Number> void callV3(List<A> list, Object elem) {
            A o1 = (A) elem;
            System.out.println(o1);
        }
    
        public static void main(String[] args) {
            call(new ArrayList<Integer>(), "trying to cast string to Integer");
            callV2(new ArrayList<Integer>(), "trying to cast string to Integer");
    
            //will be casted to Number
            callV3(new ArrayList<Integer>(), 1);
            callV3(new ArrayList<Double>(), 1.5);
            callV3(new ArrayList<Long>(), 1L);
    
            // following will fail at runtime with ClassCastException
            /* Exception in thread "main" java.lang.ClassCastException: 
               class java.lang.String cannot be cast to class java.lang.Number 
              (java.lang.String and java.lang.Number are in 
               module java.base of loader 'bootstrap')
            */
            callV3(new ArrayList<Integer>(), "trying to cast string to Integer");
        }
    
    }
    

    【讨论】:

    • 谢谢!那么在callV3中,A o1 = (A) elem会被编译成Number o1 = (Number) elem
    【解决方案2】:

    不,因为类型信息在 java 中仅在运行时可用,因此它不会编译为比Object 更具体的任何内容。其他情况是,如果类型参数 T 已绑定,例如:

     <T extends Number> void call(List<T> list, Object o) {
         fun((T) o);
     }
    

    然后编译器在编译时不知道确切的类型,但它知道它是Number 的子类型,所以如果方法fun 使用Number 作为参数,它将编译而不是第一个例子。

    您可以找到更多信息here

    【讨论】:

    • 感谢您回答我的问题!但我不完全理解答案。首先,在您的示例中,代码将编译为fun((Number) o) 还是仅编译为fun(o)?其次,我不明白fun 消耗Number 的声明的意义,我迷路了,因为我认为fun 只消耗一个参数,那么第二个参数在哪里?
    • 1.是的,这就是结果 - fun((Number) o)。 2.对不起,我的意思是参数,更正了答案。
    【解决方案3】:

    会编译成

    不,如果绑定为Object,则不会插入任何类型转换,因为对Object 的类型转换总是成功的。

    如果类型变量是有界的,例如T extends Number,然后会插入一条checkcast 指令以确保oNumber,相当于(Number) o

    您可能会注意到编译器会在该行生成未经检查的警告。未经检查的警告意味着编译器无法插入字节码以确保oT 的实例,具体而言。


    还值得指出的是类型变量是多余的。您可以使用以下内容,这将是等效的:

    void call(List<?> list, Object o) {
        fun(o);
    }
    

    强制转换是不必要的,因为无论如何fun 必须能够接受任何Object

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-18
      • 1970-01-01
      相关资源
      最近更新 更多