泛型
1.为什么要有泛型(存在的意义)
对于集合,他可以存储各种类型的对象,正因为它的抽象,导致使用时会出现混乱
泛型:在声明,创建 对象时,将抽象具体化
Collection<E>,List<E>,ArrayList<E> 这个<E>就 是类型参数,即泛型。
把元素的类型设计成一个 参数,这个类型参数叫做泛型。
1.1 泛型的概念
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类 型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如, 继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实 际的类型参数,也称为类型实参)
把一个集合中的内容限制为一个特定的数据类型,这就是generics背后 的核心思想
1.2 泛型存在的意义
1. 解决元素存储的安全性问题,好比商品、药品标签,不会弄错。
2. 解决获取数据元素时,需要类型强制转换的问题,好比不用每回拿商品、药品都要辨别。
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException异常。同时,代码更加简洁、健壮。(泛型在编译时就进行了检查)
2.在集合中使用泛型(方法和注意)
3.自定义泛型(方法)
3.1 自定义泛型
1.泛型声明
interface<T,K> 不代表值,表示类型,在声明这里使用任意字母都可以
2.泛型实例化
一定要在类名后面指定类型参数的值(类型):
List<Integer> strList = new ArrayList<Integer>();
T只能是类,不能用基本数据类型填充。但可以使用包装类填充
3.2 自定义泛型类,接口
1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如: <E1,E2,E3>
2. 泛型类的构造器如下:public GenericClass(){}。
而下面是错误的:public GenericClass<E>(){}
3. 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
4. 泛型不同的引用不能相互赋值。
>尽管在编译时ArrayList<String>和ArrayList<Integer>是两种类型,但是,在运行时只有 一个ArrayList被加载到JVM中。
5. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价 于Object。 经验:泛型要使用一路都用。要不用,一路都不要用。
6. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
7. jdk1.7,泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>();
8. 泛型的指定中不能使用基本数据类型,可以使用包装类替换。Integer
9. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态 属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法 中不能使用类的泛型。
10. 异常类不能是泛型的
11. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
12.父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
12.1子类不保留父类的泛型:按需实现
没有类型 擦除
具体类型 : 实现成具体类型
12.2子类保留父类的泛型:泛型子类
全部保留
部分保留
结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自 己的泛型
3.3 自定义泛型方法
方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。
在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。
泛型方法的格式:
[访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
返回类型和传入参数都是抽象的,在传入时指定类型,整个方法就是该类型的方法
4.泛型在继承上的体现
如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G<B>并不是G<A>的子类型!
5.通配符的使用
1.意义
抽象的运用,一对多
2.限制
只能读不能写(null除外)
3.写的方法
先get赋给Object,再来set
4.是什么
使用类型通配符:?
比如:List<?> ,Map<?,?>
List<?>是List<String>、List<Object>等各种泛型List的父类
5.注意
1.不能在方法声明的返回上使用
2.不能用在泛型类的声明上: ?是已经生成的泛型的抽象,不是创建者,而是运用者
3.创建对象时,必须指定具体类型,不能使用?
6. 通配符? 的使用场景
6.1 <?> 允许所有泛型的引用调用
6.2 通配符指定上限
上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
6.3 通配符指定下限
下限super:使用时指定的类型不能小于操作的类,即>=
6.4 举例:
1. <? extends Number> (无穷小 , Number] 只允许泛型为Number及Number子类的引用调用
2. <? super Number> [Number , 无穷大) 只允许泛型为Number及Number父类的引用调用
3. <? extends Comparable> 只允许泛型为实现Comparable接口的实现类的引用调用
理解方法: 当我们向泛型指定具体类型时,?所代表的那一类类型必须满足所有情况,所以进行了整体的限定
6.泛型应用举例
个人理解:
从集合出发: 集合可以存储任何类型的对象,集合相当于一种抽象的操作,他可以应用于许多具体类型
所以使用泛型的第一个前提: 需要一个抽象的操作,对于不同的类型都可以进行该操作
指定具体的类型,在未使用这些操作之前,我们传入具体类型,实现从抽象到具体
思想: 本质是O-O编程的思想: 共同的事物就将其抽象出来,只需要实现一次就可以供每一个具体的事物使用