泛型知识点总结

目录

泛型的概念:

泛型的底层实现

什么是类型擦除:

Java编译器具体是如何擦除泛型的

对于泛型的理解:

泛型的使用注意点(零散知识点1):

泛型的使用注意点(零散知识点2):

泛型方法:

类型变量的限定:


泛型的概念:

泛型的底层实现

        我们知道,Java有Java编译器和Java虚拟机,编译器会将Java源代码转换成 .class文件,然后虚拟机加载并运行 .class文件。然而对于泛型类,Java编译器会将泛型代码转换为普通的非泛型代码,(就像没有指定泛型时的普通代码)将泛型类型参数擦除,如何擦除的呢,即将泛型参数替换为object,然后插入必要的强制类型转换。Java虚拟机实际执行的时候,他是不知道泛型这回事的,只知道普通的类及代码。

        泛型这个设计是在jdk5.0之后才出现的,这个设计是为了兼容性而不得已的一个选择,而使用泛型会有更好的安全性、更好的可读性。通过使用泛型,开发环境和编译器能确保不会用错类型,为程序多设置一道安全网,还省去了类型的强制转换。使得运行时可能出现的异常提前到编译时。

        Java的泛型是通过擦除实现的,类定义中的类型参数如果没有指定特定类型,就会被替换为object,如果指定了参数类型,就会被替换为指定类型。在程序运行的过程中,虚拟机是不知道泛型的实际类型参数,比如pair <Integer>,运行只知道pair,而不知道Integer,所以Java的泛型又称为伪泛型。

什么是类型擦除:

(对于虚拟机而言,并没有泛型这一说法,编译器会使用类型擦除来处理泛型类变成普通类。)

无论何时定义一个泛型类型,都会自动提供一个相应的原始类型,这个原始类型的名字就是去掉类型参数后的泛型类型名,类型变量会被擦除并替换为其限定类型,(或者对于无限的变量则替换为object)。例如:在程序中有不同类型的Pair。例如Pair<String>、Pair<LocalDate>。这些在擦除类型后都会变成原始的Pair类型,然而原始类型会用第一个限定来替换类型变量 如上文示例中会替换为Comparable而不是Serializable,或者,如果没有给限定,就替换为object类型,即Pair<object>。然后在必要的时候会使用强制类型转换,使虚拟机识别的来。

Java编译器具体是如何擦除泛型的

  1. 检查泛型类型,获取目标类型
  2. 擦除类型变量,并替换为限定类型
    如果泛型类型的类型变量没有限定(<T>),则用Object作为原始类型
    如果有限定(<T extends XClass>),则用XClass作为原始类型
    如果有多个限定(T extends XClass1&XClass2),则使用第一个边界XClass1作为原始类
  3. 在必要时插入类型转换以保持类型安全

对于泛型的理解:

        泛型可以理解为一种规范、一个标签(即把元素的类型设计成一个参数,这个类型参数叫做泛型),比如:中药店装药的盒子上会有一个空标签,不同的药对应不同的标签,标签为枸杞的盒子只装枸杞,标签为板蓝根的盒子只装板蓝根,装药的盒子上的名字就是一种规范,其中空标签就是泛型参数E,不同的标签名就是对应的指定类型。

       泛型是计算机程序的一种重要的思维方式,它将数据结构与算法和数据类型相分离,使得同一套数据结构和算法可以应用于不同类型的数据类型,而且可以保证类型安全,提高可读性。

       在java中泛型是通过擦除实现的他是java编译器的概念,java虚拟机运行时对泛型基本一无所知。什么是擦除方式呢?后文会提及;

泛型的使用注意点(零散知识点1):

1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内,用逗号隔开。比如:<E1,E2,E3>

2. 在使用泛型之后引入了泛型参数,很多人就以为泛型类很特别,经常会认为泛型类的构造器是这样的   public GenericClass<E>();实际上泛型类的构造参数是这样的 public GenericClass()。他们忽略了在虚拟机的眼里泛型类和基本类毫无区别,编译时编译器会将泛型类以擦除的方式变成非泛型类,所以又称Java的泛型是伪泛型,意思也就是说java虚拟机不认识泛型;

泛型类错误的构造方法:     public GenericClass<E>();

3.实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。

       这句话的意思很难理解举个例子就很清晰了,对于ArrayList<string>集合中只能存储尖括号中指定的泛型类型的数据,即ArrayList<string>中不能存储double类或者其他非String类型的数据;

4.泛型要使用一路都用。要不用,一路都不要用。

5.泛型如果不指定,将被擦除(就是采用默认为object的方式),泛型对应的类型均按照Object处理,但不等价于Object。

         意思也就是说上面两种方式是不等价的;

6.如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。

7.jdk1.7,泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>();

即在前面尖括号中指定了泛型类型后,后面的尖括号中可以不指定泛型类型

8.泛型的指定中不能使用基本数据类型,可以使用包装类替换。

        为什么会这样呢?当我们在使用泛型的时候如果不指定它的具体类型那么他默认是object类型,然而object类型是所有引用类型的根,所以我们在指定泛型的时候只能使用引用类型包装类也是属于引用类型体系的;

9.在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。

10.异常类不能是泛型的

11.不能使用new E[ ]。但是可以: E[ ] elements = (E[ ])new Object[capacity](因为E只是一个参数,不是类,你不能直接上来new一个未知参数的数组);参考:ArrayList源码中声明:Object[ ]   elementData,而非泛型参数类型数组。。

12,

结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型

泛型的使用注意点(零散知识点2):

  • 集合接口或集合类在就jdk5.0后修改为带泛型。
  • 实例化集合类时,可以指明具体类型。
  • 指定完数据类型后,在集合类或接口中凡是定义类或接口时,内部结构(方法、构造器、属性)使用到泛型的结构时,都指定为实例化时的泛型类型。
  • 如果实例化时没有用到泛型,系统会(通过擦除的方式实现)默认泛型是java.lang.object类型。
  • 如果用一个明确的类型而不是var声明的一个变量,则可以通过使用菱形语法省略构造器中的类型参数。
  • 在定义了泛型后,我么可以用具体的数据类型替换类型变量来实例化泛型类型,不同的类型变量放到泛型方法中形成不同的普通类。

泛型方法:

泛型方法可以在普通类中定义,也可以在泛型类中定义。注意,类型变量放在修

(这里的修饰符就是public static)的后面,并且在返回类型的前面。

这里值得注意的是,有些情况下我们方法的返回值也可能是泛型参数,那么此时就要格外注意了:以下这个是错误写法:

正确写法如下:

 为什么要在E前面加一个<E>呢?因为,我们的编译器是不知道泛型的,他首先会将E默认为一个class类处理,所以会报错,加上一个<E>是为了标识E是泛型参数而非class类;

类型变量的限定:

例:public static <T extends Comparable & Serializable> T min(T [ ] a)……

 类型变量的限定是指,使类型变量T去继承或实现其他接口或类,这里只会使用extends关键字,原因:他更接近子类型的概念,这是因为java的设计者不打算在另外添加一个新的关键字。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值