java的泛型擦除啷个回事罗

泛型擦除啷个回事罗

  • 泛型擦除
    • List中的泛型擦除(例子)
      • 反编译

岁暮阴阳催短景,天涯霜雪霁寒宵。——唐代王之涣《登鹳雀楼》

泛型擦除

当我们通过get方法获取泛型是Integer的List中的第一个元素时,返回值会被自动转换为Integer类型, Java的自动类型转换的过程, 为什么泛型擦除了之后还能获取的到原来的类型

首先,我们要了解一下Java泛型和类型擦除的概念。泛型是Java中一种泛化类型的机制,它允许我们在类、接口和方法中定义类型参数,使得代码可以更灵活地复用。在编译期间,泛型信息会被擦除,即类型参数会被替换为其边界类型(如果有边界类型)或Object类型。类型擦除主要是为了保持向后兼容性,因为在Java泛型引入之前,已经存在的代码都是使用原始类型的。


List中的泛型擦除(例子)

现在我们来看一下,当我们通过get方法获取泛型为IntegerList中的第一个元素时,为什么返回值会被自动转换为Integer类型。

假设我们有一个泛型为IntegerList,代码如下:


List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);

当我们调用get方法获取第一个元素时,这里的过程分为两个阶段:编译时和运行时。

  1. 编译时:泛型信息被擦除,get方法的返回类型变为Object。编译器会在编译期间检查类型安全,以确保intList只包含Integer类型的元素。此外,编译器还会在需要的地方插入类型转换代码。在这个例子中,我们获取到的元素的类型是Object,但是编译器知道它实际上是一个Integer,所以会在适当的地方插入一个类型转换,将Object类型转换为Integer类型。

Integer firstElement = (Integer) intList.get(0);

  1. 运行时:当代码运行时,类型擦除已经发生,所以get方法返回的是一个Object类型的对象。但由于编译器在编译期间已经插入了类型转换代码,所以这个Object类型的对象会被自动转换为Integer类型。

总结一下,虽然泛型信息在编译时被擦除,但编译器会在编译期间检查类型安全,并在需要的地方插入类型转换代码。这样,在运行时,虽然泛型信息已经被擦除,但仍然可以获取到原来的类型,因为类型转换是在编译期间自动插入的。


反编译

验证一下:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello world!");
        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);
        intList.add(3);
        Integer firstElement = intList.get(0);
        System.out.println("firstElement: " + firstElement);
    }
}

反编译:

javap -c -p -v Main.class

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello world!
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: new           #5                  // class java/util/ArrayList
        11: dup
        12: invokespecial #6                  // Method java/util/ArrayList."":()V
        15: astore_1
        16: aload_1
        17: iconst_1
        18: invokestatic  #7                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        21: invokeinterface #8,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        26: pop
        27: aload_1
        28: iconst_2
        29: invokestatic  #7                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        32: invokeinterface #8,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        37: pop
        38: aload_1
        39: iconst_3
        40: invokestatic  #7                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        43: invokeinterface #8,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        48: pop
        49: aload_1
        50: iconst_0
        51: invokeinterface #9,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
        56: checkcast     #10                 // class java/lang/Integer
        59: astore_2
        60: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        63: new           #11                 // class java/lang/StringBuilder
        66: dup
        67: invokespecial #12                 // Method java/lang/StringBuilder."":()V
        70: ldc           #13                 // String firstElement:
        72: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        75: aload_2
        76: invokevirtual #15                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
        79: invokevirtual #16                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        82: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        85: return

我们看到这两行

51: invokeinterface #9, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
56: checkcast #10 // class java/lang/Integer

获取其实是一个object, 编译器自动帮我们推测了类型, 并且自动帮我们加上了强制类型转换

你可能感兴趣的:(java基础,java,jvm,数据结构)