[英]How type erasure works for wildcard in java?
As mentioned in javadoc, 如javadoc中所述,
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded.
如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或对象。 The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
因此,生成的字节码仅包含普通的类,接口和方法。
Insert type casts if necessary to preserve type safety.
如有必要,插入类型铸件以保持类型安全。
I understand on above two points that are handled using javac
for type parameters like T
and <K, V>
. 我理解上面两点使用
javac
处理类型参数,如T
和<K, V>
。
But for wild cards in below methods, 但对于以下方法中的外卡,
public static void printList(List<?> list) {
for (Object elem: list)
System.out.print(elem + " ");
System.out.println();
}
public void sumOfNumbers(List<? extends Number> numbers){
double d = 0.0;
for(Number n: numbers){
d += n.doubleValue();
}
System.out.println(d);
}
In case of wild cards, 如果是外卡,
1) 1)
How does javac
deal with these wildcards? javac
如何处理这些通配符? Does it replace with something? 它取代了什么吗?
2) 2)
How type casts are performed? 如何进行类型转换?
?
is replaced with Object
(since it has no bound) - not that useful. 被
Object
替换(因为它没有绑定) - 没有那么有用。
At build time the compiler will check you are only calling Object's behaviours. 在构建时,编译器将检查您是否只调用Object的行为。
? extends Number
? extends Number
is replaced with its bound Number
. ? extends Number
被其绑定的Number
替换。
At build time the compiler will check you are only passing a Number
or any of its subtypes as an argument. 在构建时,编译器将检查您是仅传递
Number
或其任何子类型作为参数。
Casting - No cast. 铸造 - 无铸造。 At build time the compiler will check you are only calling behaviours which are permissible for a
Number
. 在构建时,编译器将检查您是否只调用
Number
允许的行为。
T
is replaced with whatever type you have provided for the type parameter of the class. T
将替换为您为类的类型参数提供的任何类型。
At build time the compiler will check you are only passing a type T
whatever that was, as an argument (is using T
as a parameter in your method). 在构建时,编译器将检查您是否仅传递类型
T
作为参数(在方法中使用T
作为参数)。
?
and T
have different uses. 和
T
有不同的用途。
Think T
for generic Type (Classes, Interfaces) creation - which can then be referred to anywhere in the type. 想想
T
的泛型类型(类,接口)创建 - 然后可以在类型中的任何地方引用它。
Think ?
想
?
as a way of limiting what types you can legally invoke a method with at Compile time. 作为一种限制类型的方法,您可以在编译时合法地调用方法。
Wildcards only differ from named type parameters at compile time as the compiler will try to enforce that types using the same named parameter are indeed the same. 通配符仅在编译时与命名类型参数不同,因为编译器将尝试使用相同的命名参数强制执行这些类型确实是相同的。
But if you look at this source code: 但是如果你看一下这个源代码:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void foo(List<? extends Number> first, List<? extends Number> second) {
Number n = first.get( 0 );
Number m = second.get( 0 );
List<Number> third = new ArrayList<Number>();
third.add( n );
third.add( m );
System.out.println( third );
}
public static <T extends Number> void bar(List<T> first, List<T> second) {
T n = first.get( 0 );
T m = second.get( 0 );
List<T> third = new ArrayList<T>();
third.add( n );
third.add( m );
System.out.println( third );
}
}
...and the decompiled bytecode: ...和反编译的字节码:
Compiled from "Test.java"
public class Test {
public Test();
Code:
public static void foo(java.util.List<? extends java.lang.Number>, java.util.L
ist<? extends java.lang.Number>);
Code:
0: aload_0
1: iconst_0
2: invokeinterface #2, 2 // InterfaceMethod java/util/List.ge
t:(I)Ljava/lang/Object;
7: checkcast #3 // class java/lang/Number
10: astore_2
11: aload_1
12: iconst_0
13: invokeinterface #2, 2 // InterfaceMethod java/util/List.ge
t:(I)Ljava/lang/Object;
18: checkcast #3 // class java/lang/Number
21: astore_3
22: new #4 // class java/util/ArrayList
25: dup
26: invokespecial #5 // Method java/util/ArrayList."<init
>":()V
29: astore 4
31: aload 4
33: aload_2
34: invokeinterface #6, 2 // InterfaceMethod java/util/List.ad
d:(Ljava/lang/Object;)Z
39: pop
40: aload 4
42: aload_3
43: invokeinterface #6, 2 // InterfaceMethod java/util/List.ad
d:(Ljava/lang/Object;)Z
48: pop
49: getstatic #7 // Field java/lang/System.out:Ljava/
io/PrintStream;
52: aload 4
54: invokevirtual #8 // Method java/io/PrintStream.printl
n:(Ljava/lang/Object;)V
57: return
public static <T extends java.lang.Number> void bar(java.util.List<T>, java.ut
il.List<T>);
Code:
0: aload_0
1: iconst_0
2: invokeinterface #2, 2 // InterfaceMethod java/util/List.ge
t:(I)Ljava/lang/Object;
7: checkcast #3 // class java/lang/Number
10: astore_2
11: aload_1
12: iconst_0
13: invokeinterface #2, 2 // InterfaceMethod java/util/List.ge
t:(I)Ljava/lang/Object;
18: checkcast #3 // class java/lang/Number
21: astore_3
22: new #4 // class java/util/ArrayList
25: dup
26: invokespecial #5 // Method java/util/ArrayList."<init
>":()V
29: astore 4
31: aload 4
33: aload_2
34: invokeinterface #6, 2 // InterfaceMethod java/util/List.ad
d:(Ljava/lang/Object;)Z
39: pop
40: aload 4
42: aload_3
43: invokeinterface #6, 2 // InterfaceMethod java/util/List.ad
d:(Ljava/lang/Object;)Z
48: pop
49: getstatic #7 // Field java/lang/System.out:Ljava/
io/PrintStream;
52: aload 4
54: invokevirtual #8 // Method java/io/PrintStream.printl
n:(Ljava/lang/Object;)V
57: return
}
...you can see that the same checkcast #3
opcode is used in both methods, checking only that the values retrieved from the list can be cast to Number
, but not if they are of the same type. ...你可以看到在两种方法中使用相同的
checkcast #3
操作码,只检查从列表中检索的值是否可以checkcast #3
为Number
,但如果它们属于同一类型则不能。
And when you think about it, such check would be impossible in general due to type erasure. 当你考虑它时,由于类型擦除,这种检查通常是不可能的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.