Java泛型和编译优化的一个例子

public class Main {
    public static void main(String[] args) {
        ArrayList<String> strList = new ArrayList<String>();
        Type type = ((ParameterizedType)strList.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        System.out.println(type);
    }
}
public class Main {
    public static void main(String[] args) {
        ArrayList<String> strList = new ArrayList<String>(){};
        Type type = ((ParameterizedType)strList.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        System.out.println(type);
    }
}

这两个例子唯一的区别是后者的new ArrayList<String>(){}初始化strList的时候带了{}执行了赋初值,虽然语法层面没有什么区别,但是在编译之后的结果却完全不一样。而且执行的结果也完全不一样,
前者执行结果:

E

后者执行结果:

class java.lang.String

前者的编译结果:

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #2                  // class java/util/ArrayList
         3: dup
         4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
        12: invokevirtual #5                  // Method java/lang/Class.getGenericSuperclass:()Ljava/lang/reflect/Type;
        15: checkcast     #6                  // class java/lang/reflect/ParameterizedType
        18: invokeinterface #7,  1            // InterfaceMethod java/lang/reflect/ParameterizedType.getActualTypeArguments:()[Ljava/lang/reflect/Type;
        23: iconst_0
        24: aaload
        25: astore_2
        26: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        29: aload_2
        30: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        33: return

这个逻辑很简单,就是简单的invokespecialArrayList<init>()方法。

后者的编译结果:

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #2                  // class Main$1
         3: dup
         4: invokespecial #3                  // Method Main$1."<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
        12: invokevirtual #5                  // Method java/lang/Class.getGenericSuperclass:()Ljava/lang/reflect/Type;
        15: checkcast     #6                  // class java/lang/reflect/ParameterizedType
        18: invokeinterface #7,  1            // InterfaceMethod java/lang/reflect/ParameterizedType.getActualTypeArguments:()[Ljava/lang/reflect/Type;
        23: iconst_0
        24: aaload
        25: astore_2
        26: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        29: aload_2
        30: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        33: return

这里就奇怪了,加了{}之后竟然生成了内部类Main$1:

final class Main$1 extends java.util.ArrayList<java.lang.String>
...
{
  Main$1();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/util/ArrayList."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0
}
Signature: #9                           // Ljava/util/ArrayList<Ljava/lang/String;>;
...

java.util.ArrayList<java.lang.String>的子类,这也就解释了后者的执行结果为何可以解析到strList的泛型参数化类型是java.lang.String了,因为它的实际类型在JVM执行的时候清楚地被标记成了内部类Main$1这个java.util.ArrayList<java.lang.String>的子类。而前者的strList的泛型参数化类型已经被擦除掉了。

原文地址:https://www.cnblogs.com/twodog/p/12136096.html