简体   繁体   English

当类型参数用于强制转换时,Java 类型擦除如何工作?

[英]How does Java type erasure work when the type parameter is used for casting?

I know generally unbound type parameters are substituted with Object in compile time.我知道通常未绑定的类型参数在编译时被Object替换。 But how does this piece of code work?但是这段代码是如何工作的呢?

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

Will it be compiled to会编译成

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

Which seems like a wrong case because o should be cast to the same type as the elements in list?这似乎是一个错误的情况,因为o应该转换为与列表中的元素相同的类型?

Because of type erasure even if your type is A , it will be treated as Object in your example as you guessed.由于类型擦除,即使您的类型是A ,在您的示例中它也将被视为Object ,正如您所猜测的那样。 which means if you passed your second elem other than A , it will actually be casted to Object .这意味着如果您通过了A以外的第二个元素,它实际上将被转换为Object

Example,例子,

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");
    }

}

No, because type information is available in java only in runtime, hence it won't compile to anything more specific than Object .不,因为类型信息仅在运行时在 java 中可用,因此它不会编译为比Object更具体的任何内容。 Other case would be, if type parameter T was bound, for example:其他情况是,如果类型参数T被绑定,例如:

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

Then the compiler doesn't know the exact type at compile time, but it does know it's a subtype of Number , so if method fun consumed Number as parameter, it would compile as opposed to the first example.然后编译器在编译时不知道确切的类型,但它确实知道它是Number的子类型,因此如果方法fun使用Number作为参数,它将与第一个示例相反进行编译。

You can find more info here .你可以在这里找到更多信息。

Will it be compiled to会编译成

No, no cast will be inserted if the bound is Object , because a cast to Object always succeeds.不,如果绑定为Object则不会插入任何类型转换,因为转换为Object总是成功的。

If the type variable were bounded, eg T extends Number , then a checkcast instruction would be inserted to ensure that o is a Number , equivalent to (Number) o .如果类型变量是有界的,例如T extends Number ,那么将插入一个checkcast指令以确保o是一个Number ,相当于(Number) o

You may notice that the compiler generates a unchecked warning on that line.您可能会注意到编译器会在该行生成未经检查的警告。 An unchecked warning means that the compiler is unable to insert a bytecode to ensure that o is an instance of T , specifically.未经检查的警告意味着编译器无法插入字节码以确保oT的实例。


It's also worth pointing out that the type variable is redundant.还值得指出的是类型变量是多余的。 You can use the following, and it will be equivalent:您可以使用以下内容,这将是等效的:

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

The cast is unnecessary, since fun must be able to accept any Object anyway.演员表是不必要的,因为fun必须能够接受任何Object无论如何。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM