当前位置: 首页 > 知识库问答 >
问题:

java中的通配符类型擦除是如何工作的?

呼延英奕
2023-03-14

正如在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)

如何执行类型转换?


共有2个答案

司知
2023-03-14

通配符仅在编译时与命名类型参数不同,因为编译器将尝试强制使用相同命名参数的类型实际上是相同的。

但是如果你看看这个源代码:

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,但如果它们属于同一类型,则不能。

当您考虑它时,由于类型擦除,这种检查通常是不可能的。

苏涵润
2023-03-14
匿名用户

<代码>?替换为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。地图