(Java基础篇都是根据《Java核心技术 卷I》再进行自己的总结归纳和思考写出的)
泛型是什么?
泛型字面上就是多种类型,也就是多种类型都可以通用的代码,它的目的就是为了实现代码的重用。
比如说打印函数,在设计打印函数的时候我们要考虑到,可能传入的参数类型是各种各样(String、Integer...)的,这时候就是我们所说的“通用代码”。打印函数就可以利用泛型,设计一个对任意数据类型通用的函数。泛型的实现原理其实是省略了参数类型-->Object类型-->参数类型的这个过程。若不使用泛型,可以通过将函数传入的参数定为Object类型(也可以是对应类型的父类),那么也是一种通用的代码,因为Object是所有类的超类,但是这样设计函数会使得传入非预期的类型对象也能够执行此函数,并且还有经常强转的麻烦。
如果使用泛型,就能够记住传入的参数类型,省却了手动类型转换的这一个过程。
但是要记住一点!!!在实际虚拟机执行过程中是走了这个转换过程的。
使用泛型
此处使用泛型是指使用别人实现的泛型类/方法。
使用泛型类/方法是第一个阶段,因为大多数用到泛型的类都已经封装好了,许多程序也用不到新设计和实现泛型类和方法。
使用泛型类是比较简单学习的,它就像普通类的工厂。
具体的使用可以参照集合类ArrayList、Map等用法,其实就是如何声明,并使用里面的方法的问题。
使用泛型产生的效果就是限制了相应的数据类型。
实现和设计泛型类/方法
为了让这个类/方法能够被多种类型重用而在类和方法上添加泛型,那么首先我们要明白泛型在底层的逻辑
-
底层有关Java泛型转换的两个事实:
- 虚拟机里没有泛型,只有普通的类和方法
虚拟机不会有泛型的概念,就只有哪个类型就是哪个类型 - 所有类型参数都用他们的限定类型替换
这就解释了虚拟机怎么去翻译相应的泛型类和泛型方法,它都是把里面的泛型翻译到限定的类型,一般没有限定都是以Object类型来翻译泛型
这就是类型擦除。就是我们所设计的泛型到虚拟机里面都会被擦除成为普通的类和方法。
- 虚拟机里没有泛型,只有普通的类和方法
知道在虚拟机中泛型会被类型擦除,那么下面的很多程序设计上的问题都是由类型擦除带来的
-
泛型设计的局限
- 不能用基本类型实例化类型参数:因为基本类型没办法擦除嘛
- 运行时类型查询只适用于原始类型:是不能直接判断一个泛型类对象(如ArrayList<String>),里面的具体是什么类型的
- 不能创建参数化类型的数组:声明是可以,但是创建不了ArrayList<String>[10],但是可以创建ArrayList<?>[10],以通配符的形式。原因还是类型擦除。
- 不能实例化类型变量:new T(...)是不能用的,因为这样到虚拟机里面擦除之后都变成Object,没意义了。
- 不能构造泛型数组:不能new T[2]这样的写法
- 泛型类的静态上下文中类型变量无效:这里可以参考博客,讲的很好
- ...
这里列举了六种会比较常遇到的问题,也简略解释了一下,如果有不明白的可以参考《Java核心技术 卷I》泛型程序设计里面的约束和局限这一小节
-
泛型类&泛型方法有什么区别?
泛型类里面也可以有泛型的方法,但是呢类也可以不是泛型的而单单方法是泛型的,它们用法有什么区别?
其实泛型类的优势就是能够把类里面所有的方法泛型都给统一了,也就是说一旦类的参数化类型定下来了,这个对象所有的方法都必须是这个类型。单单是方法是泛型就没有这个效果。
-
泛型的继承
-
这里需要搞清楚的是,泛型类里面的参数化类型之间的继承关系不是泛型类之间的继承关系:
ArrayList<father>和ArrayList<son>没有继承关系
List<>和ArrayList<>才有继承关系:继承关系意义就是孩子把父亲更具体化了
通配符
通配符的概念很容易让人将泛型 T 和通配符 ? 搞晕。通配符?的目的呢就是解决上面所说的ArrayList<father>和ArrayList<son>问题。
假如现在有两个对象:
ArrayList<father> a; ArrayList<son> b;
那么我只能往a对象里面放入father类型的对象,只能往b对象放入son类型的对象。但是呢如果是ArrayList<? extends father> c,那么就可以既存father也可以存son,
但是实验好像有点问题,嗯,我下次专门写一个探究一下通配符问题 -