简体   繁体   English

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

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

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM