类型擦除如何在java中使用通配符?

时间:2015-07-23 09:44:11

标签: java generics javac

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

如何执行类型转换?

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,但如果属于同类型。

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