导图社区 java泛型
介绍了java泛型概念原理分类,和适用场景,以及注意点,和一些重要知识点剖析
编辑于2019-10-16 15:52:37泛型
what
概念
泛型实现原理什么
Java泛型是通过类型擦除实现的,它是Java编译器的概念,Java虚拟机运行时对泛型基本一无所知,类定义中的类型参数如T会被替换为Object,在程序运行过程中,不知道泛型的实际类型参数
T是什么
T表示类型参数,泛型就是类型参数化,处理的数据类型不是固定的,而是可以作为参数传入
类型参数
多个类型之间以逗号分隔
形如DynamicArray<T>
形如DynamicArray<T extends E>
通配符
有限定通配符
?表示通配符,<?extends E>表示有限定通配符,匹配E或E的某个子类型,具体什么子类型是未知的
只能读,不能写
无限定通配符
形如DynamicArray<?>
超类型通配符
形式为<?super E>
有了它,我们就可以更灵活地写入了
通配符比较
<?super E>用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类对象,它不能被类型参数形式替代
<?>和<?extends E>用于灵活读取,使得方法可以读取E或E的任意子类型的容器对象,它们可以用类型参数的形式替代,但通配符形式更为简洁
上界
上界为某个具体类
限定类型后,就可以使用该类型的方法
上界为某个接口
直接这么编写代码,Java中会给一个警告信息,因为Comparable是一个泛型接口,它也需要一个类型参数
递归类型限制
上界为其他类型参数
分类/层级
按类型
泛型类
泛型方法
类型参数为T,放在返回值前面
和泛型类一样类型参数可以有多个,以逗号分隔
泛型接口
优缺点
好处
更好的安全性
开发环境(如Eclipse)会提示类型错误
更好的可读性
明确的类型信息,代码可读性也会更好
Where
能用场景
不能用场景
基本类型不能用于实例化类型参数
使用基本类型对应的包装类
运行时类型信息不适用于泛型
instanceof是运行时判断,也与泛型无关
不支持类似如下写法
if(p1 instanceof Pair<Integer>)
支持如下写法
if(p1 instanceof Pair<?>)
类型擦除可能会引发一些冲突
因为类型擦除后,实际上只能有一个
不能通过类型参数创建对象
非法的
那如果确实希望根据类型创建对象呢?
需要设计API接受类型对象,即Class对象,并使用Java中的反射机制
泛型类类型参数不能用于静态变量和方法
非法的
不能创建泛型数组
可以使用原始类型的数组
更好的选择是泛型容器
内部的数组为Object类型,一些操作插入了强制类型转换,外部接口是类型安全的,对数组的访问都是内部代码,可以避免误用和类型异常
泛型容器内部使用Object数组,如果要转换泛型容器为对应类型的数组,需要使用反射
When
哪个时间点开始的
泛型是Java 5以后才支持的
Why
为什么使用泛型
类、接口和方法代码可以应用于非常广泛的类型,代码与它们能够操作的数据类型不再绑定在一起,同一套代码可以用于多种数据类型,这样,不仅可以复用代码,降低耦合,而且可以提高代码的可读性和安全性
为什么设计泛型
为了兼容性而不得已的一个选择
语言和程序设计的一个重要目标是将bug尽量消灭在摇篮里
其他点的剖析
<T extends E>和<?extends E>到底有什么关系
<T extends E>用于定义类型参数,它声明了一个类型参数T,可放在泛型类定义中类名后面、泛型方法返回值前面
)<?extends E>用于实例化类型参数,它用于实例化泛型变量中的类型参数,只是这个具体类型是未知的,只知道它是E或E的某个子类型
问号就是表示类型安全无知,?extends Number表示是Number的某个子类型,但不知道具体子类型,如果允许写入,Java就无法确保类型安全性,所以干脆禁止
由于类型擦除,Java只能创建Object类型的对象,而无法创建T类型的对象,容易引起误解,所以Java干脆禁止这么做
如果合法,那么对于每种实例化类型,都需要有一个对应的静态变量和方法。但由于类型擦除,Singleton类型只有一份,静态变量和方法都是类型的属性,且与类型参数无关,所以不能使用泛型类类型参数
不过,对于静态方法,它可以是泛型方法,可以声明自己的类型参数,这个参数与泛型类的类型参数是没有关系的
Watch
注意点
一个方法是不是泛型的,与它所在的类是不是泛型没有什么关系
与泛型类不同,调用方法时一般并不需要特意指定类型参数的实际类型
实现接口时,应该指定具体的类型
限定类型后,如果类型使用错误,编译器会提示。指定边界后,类型擦除时就不会转换为Object了,而是会转换为它的边界类型
虽然Integer是Number的子类,但DynamicArray<Integer>并不是DynamicArray<Number>的子类
可以通过类型限定来解决
更为简洁的通配符形式
通配符有一个重要的限制:只能读,不能写
借助带类型参数的泛型方法可以解决
如果类型参数之间有依赖关系,或者返回值依赖类型参数,或者需要写操作,则只能用类型参数
通配符形式都可以用类型参数的形式来替代
超类型通配符,则无法用类型参数替代
参数限定只有extends形式,没有super形式
Java中还支持多个上界,多个上界之间以&分隔
Base为上界类,Comparable和Serializable为上界接口。如果有上界类,类应该放在第一个,类型擦除时,会用第一个上界替换