正如在javadoc提到的,
如果类型参数是无界的,则用其边界或对象替换泛型类型中的所有类型参数。因此,生成的字节码只包含普通类、接口和方法。
如果需要,插入类型转换以保持类型安全。
我理解以上两点,使用javac处理类型参数,如T和
但是对于下面方法中的百搭牌,
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)
如何执行类型转换?
通配符仅在编译时与命名类型参数不同,因为编译器将尝试强制使用相同命名参数的类型实际上是相同的。
但是如果你看看这个源代码:
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
,但如果它们属于同一类型,则不能。
当您考虑它时,由于类型擦除,这种检查通常是不可能的。
<代码>?替换为Object
(因为它没有绑定)-没有那么有用。
在构建时,编译器将检查您只调用Object的行为。
?扩展编号
替换为其绑定编号
编译时,编译器将检查您是否仅将编号
或其任何子类型作为参数传递。
铸造-没有铸造。在构建时,编译器将检查您只调用允许的行为。
T
替换为您为类的类型参数提供的任何类型
编译时,编译器将检查您是否仅将类型T
作为参数传递(在方法中使用T
作为参数)。
和
T
有不同的用途
为泛型类型(类、接口)创建考虑T
,然后可以在类型中的任何位置引用泛型类型。
想<代码>?作为一种限制在编译时可以合法调用方法的类型的方法。
问题内容: 我以为Java擦除会在编译时消除泛型类型,但是当我自己对其进行测试时,我意识到在Bytecode中有一些有关泛型类型的信息。 这是我的测试: 我写了2节课: 和 我编译了两个类,并在通用类的某个地方看到了这一行 在非泛型类中: 所以很明显我在字节码中有通用信息,那么这个擦除的东西是什么? 问题答案: 一些通用类型信息存储在属性中。请参阅JLS 4.8 和4.6以及JVM规范4.3.4。
问题内容: 类型擦除应该擦除所有泛型信息…如果是这种情况,那么像GSON这样的库如何使用泛型来确定反序列化为哪种类型? 例如 这将反序列 化为 将反序列化为 因此以某种方式在运行时使用通用信息。 问题答案: 类型擦除不会擦除所有类型信息。它不会从类,字段,返回类型和参数定义中将其删除。保留以下示例中的类型信息: 这是可以通过反射实现的。您可以检查给定的是否是该类,将其强制转换为该类并获取类型信息。
在Kotlin中,编译以下代码: 但是,该代码不: 编译此代码将导致以下错误: 在Java中,两个示例都无法编译: 不出所料,前面的两个代码片段都会产生熟悉的编译器错误: 令我惊讶的是,第一个 Kotlin 示例根本有效,其次,如果它有效,为什么第二个 Kotlin 示例会失败?Kotlin 是否将方法的返回类型视为其签名的一部分?此外,为什么 Kotlin 中的方法签名与 Java 相比,它遵循
通过使用泛型,我们可以在编译过程中检测到任何可能的错误。例如 当我们在Java1.5之前使用原始类型而不是泛型时,它需要显式转换。例如, 然而,对于泛型,会发生类型擦除。也就是说,类型信息在运行时丢失。如果,也就是说,JVM如何知道它在运行时检索的对象类型,是字符串对象还是person对象(上面由编译器生成的cast)。但这对泛型有效,这可能会导致运行时错误。 最后,Joshua Bloch在第1
问题内容: 有没有一种方法可以避免类型擦除并获得对类型参数的访问? 我需要了解并使用它来做事。是否可以,如果可以,如何在 不 将类传入构造函数或参数之外的任何地方的 情况下 完成操作? 编辑: 这个问题的主要目的是找出是否有 任何实用的方法 来消除类型擦除。 问题答案: 实际上, 没有 类型擦除的实用方法,因为您不能要求运行时无法访问的内容。当然,假设您同意为实现接口的每个枚举子类化通用类是一个实
我使用javap反编译了Map类。类定义仍然显示泛型类型K和V的存在。这应该被类型擦除的概念擦除。为什么没有发生这种情况? <代码>/javap-详细java。util。地图