泛型及其擦除

1. 什么是泛型,泛型具有什么优点

定义:程序编码中包含类型参数的类型,泛型适用于类,接口,方法,主要目的之一是容器持有什么类型的对象,并且有编译器来保证类型的正确性。
优点:
1. 可重用性,相同功能的但是处理不同类型的数据,可用一份泛型代码完成。换句话说即编写的代码可以被很多不同类型的对象所重用。
2. 类型安全:在编译期确保泛型类所接收的实际类型参数是正确的,不不会导致运行期的ClassCastException异常
3. 可读性:泛型可提高代码的可读性,通过参数类型就可以知道对象的参数类型。

2. 泛型在java中是如何工作的,什么是泛型的擦除

  • 泛型是用类型擦除实现,编译器在编译期间擦除所有的类型信息,具体类型在运行期是不可用的,例如List在运行期的表现形式是List,这是为了兼容jdk1.5之前的List,在运行期不需要获取类型信息,泛型在编译器运行期间被转换成原始类型。

如何实现擦除

进入擦除先看个小例子:
import java.util.ArrayList;
import java.lang.Class;
public class ErasedTypeEquivalence
{
    public static void main(String[] args)
    {
        Class c1 = new ArrayList<Integer>().getClass();
        Class c2 = new ArrayList<String>().getClass();
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c1==c2);
    }
}

编译后通过javap -verbose ErasedTypeEquivalence.class会得到该程序的编译代码,部分摘抄如下:

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
  stack=3, locals=3, args_size=1
     0: new   #2             // class java/util/ArrayList
     3: dup
     4: invokespecial #      // Method java/util/ArrayList."<in
it>":()V
    7: invokevirtual #4      // Method java/lang/Object.getClas
s:()Ljava/lang/Class;
    10: astore_1
    11: new  #2              // class java/util/ArrayList
    14: dup
    15: invokespecial #3     // Method java/util/ArrayList."<in
it>":()V
    18: invokevirtual #4     // Method java/lang/Object.getClas
s:()Ljava/lang/Class;
    21: astore_2
    22: getstatic     #5     // Field java/lang/System.out:Ljav
a/io/PrintStream;
    25: aload_1
    26: invokevirtual #6     // Method java/io/PrintStream.prin
tln:(Ljava/lang/Object;)V
    29: getstatic     #5     // Field java/lang/System.out:Ljav
a/io/PrintStream;
    32: aload_2
    33: invokevirtual #6     // Method java/io/PrintStream.prin
tln:(Ljava/lang/Object;)V
    36: getstatic     #5     // Field java/lang/System.out:Ljav
a/io/PrintStream;
    39: aload_1
    40: aload_2
    41: if_acmpne     48
    44: iconst_1
    45: goto          49
    48: iconst_0
    49: invokevirtual #7    // Method java/io/PrintStream.prin
tln:(Z)V
    52: return

通过:0: new #2 // class java/util/ArrayList我们可以得知,new ArrayList编译后的结果中并没有泛型的参数类型信息。其实,类型参数在编译的过程中被删除了。
此时会有一些疑问:
1. 泛型不是保证了类型安全吗?既然擦除了类型,那拿什么保证类型安全呢?
回答该问题前先熟悉下编译的几个阶段(有兴趣的可以回顾下编译原理的知识)
  a. 解析(parse)不输入到符号表(enter)
  b. 注解处理(annotation processing)
  c. 分析与代码生成

  • 属性标注与检查(Attr与Check)
  • 数据流分析(Flow)
  • 将泛型类型转换为裸类型(TransType)
  • 解除语法糖(Lower)
  • 生成Class文件(Gen)

类型的验证是在解析阶段进行的,如果验证不通过则会在这个阶段抛出错误,例如:List list = new ArrayList<>(); list.add(new Object());往参数类型为String的List中添加了Object类型,解析阶段就会检查到错误,而不会等到运行运行时才抛出ClassCastException。将错误的抛出大大提前了,只有在解析通过之后才会在分析和代码生成阶段将泛型转换为裸类型(即擦除)

  1. 既然擦除了参数类型信息,在list中通过get方法获取类型转换的时候为什么不需要做强制类型转换呢?例如:
    List list = new ArrayList<>();
    list.add(“generics”);
    String s = list.get(0);
    编译后的代码为:
    0: new #2 // class java/util/ArrayList
    3: dup
    4: invokespecial #3 // Method java/util/ArrayList.””:()V
    7: astore_1
    8: aload_1
    9: ldc #4 // String generics
    11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
    16: pop
    17: aload_1
    18: iconst_0
    19: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
    24: checkcast #7 // class java/lang/String
    27: astore_2
    28: return
    标红处会对将从list中获取到的对象(Object类型)转换为String。因此写代码的时不用再次进行强制转换。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值