如javadoc中所述,
如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或对象。因此,生成的字节码只包含普通的类,接口和方法。
如有必要,插入类型转换以保持类型安全。
我理解上面两点使用javac
处理T
和<K, V>
等类型参数。
但对于下面方法中的外卡,
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);
}
如果是外卡,
1)
javac
如何处理这些通配符?它取代了什么吗?
2)
如何执行类型转换?
答案 0 :(得分:1)
?
被替换为Object
(因为它没有约束力) - 没有那么有用
在构建时,编译器将检查您是否只调用Object的行为。
? extends Number
被其绑定的Number
替换
在构建时,编译器将检查您是仅传递Number
或其任何子类型作为参数。
施法 - 没有演员。在构建时,编译器将检查您是否仅调用Number
允许的行为。
T
将替换为您为类的类型参数提供的任何类型
在构建时,编译器将检查您是否仅传递类型T
作为参数(在您的方法中使用T
作为参数)。
?
和T
有不同的用途
请考虑T
创建泛型类型(类,接口) - 然后可以将其引用到类型中的任何位置。
将?
视为限制在编译时合法调用方法的类型的一种方式。
答案 1 :(得分:0)
通配符在编译时仅与命名类型参数不同,因为编译器将尝试使用相同的命名参数强制执行这些类型确实是相同的。
但是如果你看一下这个源代码:
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 );
}
}
...和反编译的字节码:
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
}
...你可以看到两种方法都使用相同的checkcast #3
操作码,只检查从列表中检索到的值是否可以强制转换为Number
,但如果属于同类型。
当你考虑它时,由于类型擦除,这种检查通常是不可能的。