简体   繁体   中英

How type erasure works for wildcard in java?

As mentioned in 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> .

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)

How does javac deal with these wildcards? Does it replace with something?

2)

How type casts are performed?

? is replaced with Object (since it has no bound) - not that useful.
At build time the compiler will check you are only calling Object's behaviours.

? extends Number ? extends Number is replaced with its bound Number .
At build time the compiler will check you are only passing a Number or any of its subtypes as an argument.

Casting - No cast. At build time the compiler will check you are only calling behaviours which are permissible for a Number .

T is replaced with whatever type you have provided for the type parameter of the class.
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).

? and T have different uses.
Think T for generic Type (Classes, Interfaces) creation - which can then be referred to anywhere in the type.

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.

And when you think about it, such check would be impossible in general due to type erasure.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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