泛型
什么是java泛型?
本质:参数化类型
即所操作的数据类型被指定为一个参数。
分泛型类、泛型接口、泛型方法
类型擦除
Java 泛型基本上都是在编译器这个层次来实现的。
在生成 Java 字节代码中(source–>bytecode 过程:编译过程)是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数会被编译器在编译的时候去掉,这就是类型擦除。(但会保证类或方法内部参数类型的一致性。即类名旁边带的 E,其内部方法使用到的 E,类型是一致的)
如 List<Object>
和 List<String>
等类型,在编译之后都会变成 List
(泛型类对象的读取(类型转换)和写入(类型检查)的位置,编译器会自动帮我们添加约束)
缺陷:泛型类型不能显式地运用在运行时类型的操作当中,例如**转型
、instanceof
和 new
**。(运行时,所有参数的类型信息都丢失了)
擦除的补偿
- 类型判断问题
1 | class Building{} |
- 创建类的实例
不能 ``new T()`原因,不能确定类型,不能确定T 是否包含无参构造函数
1 | //使用显式地工厂模式 |
为什么需要泛型?
安全简单。可将运行时错误提前到编译时错误。
泛型之前是用Object的引用来实现参数的“任意化”,这种如果强制转化错误只能在运行时发现。
设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。
通配符和上下界
通配符 ?
(只能用在方法上,不能定义在类上会报错)
上界 ? extends T
(生产者Producer)只能读,不能写(除了null);可以接收T及其所有子类类型的数据,这里T可以是类也可以是接口
下界 ? super T
(消费者Consumer)只能写,不能读;可以接收T及其所有超类类型的数据。
类型擦除将会擦除到它的第一个边界(边界可以有多个)。编译器事实上会把类型参数替换为它的第一个边界的类型。如果没有指明边界,类型将被擦除到 object
。
PECS:Producer extends Consumer super
例子:
1 | public static void main(String args[]) { |
null是所有引用类型都有的元素,所以上界(生产者)可以add成功
引入泛型之后的类型系统增加了两个维度:
类型参数自身的继承体系结构 (如
List<String>
和List<Object>
)泛型类或接口自身的继承体系结构(如
List
接口继承自Collection
接口)
**相同类型参数泛型类的关系取决于泛型类自身的继承体系结构 ** ( 即List<String>
可以替换Collection<String>
(Liskov 替换原则) )
当泛型类的类型声明中使用了通配符的时候,其子类型可以在两个维度上分别展开。
如对Collection<? extends Number>
来说,其子类型可在 Collection
这个维度上展开,即 List<? extends Number>
和 Set<? extends Number>
等。也可以在 Number
这个维度上展开,即Collection<Double>
和 Collection<Integer>
等。如此循环下去,ArrayList<Long>
和HashSet<Double>
等也算是 Collection<? extends Number>
的子类型
泛型的命名规范
E - Element,常用在 Collection 里,如:
List<E>
,Iterator<E>
,Set<E>
等K,V - Key,Value,代表 Map 的键值对
N - Number,数字
T - Type,类型
String,Integer 等 - S,U,V etc.
元组(tuple)类库
同list可用于数据存储,包含多个数据。可同时存储不同类型的数据类型。
1 | class TwoTuple<A,B>{ |
协变、逆变、不变
PECS:Producer-extends
、Consumer-super
- 对于协变
? extends T
,只能get()
,即作为生产者(Producer) - 对于逆变
? super T
,只能set()
,即作为消费者(Consumer)
可变性是一种类型安全的方式,将一个对象当做另一个对象来使用。若不能将一个类型替换为另一个类型,那么这个类型称之为:不变量。
协变与逆变是相互对立的
- 若某个返回类型可由其派生类替换,则此类型是支持协变的。(修饰返回值。把子类指向父类的关系)
- 若某个参数类型可由其基类替换,则此类型是支持逆变的。(修饰传入参数。把父类指向子类的关系)
怎么自定义泛型接口、泛型类?
当声明或者实例化一个泛型的对象时,必须指定类型参数的值:
1 | Map<String, String> map = new HashMap<String, String>(); |
数组是协变的
泛型不是协变的
例:List
不是List
的父类型
泛型若是协变的会违反泛型提供的类型安全。
例:
1 | List<Integer> intList = new ArrayList<Integer>(); |