泛型
泛型
泛型的作用
- 多种数据类型执行相同的代码
- 规范数据类型,在编译时发出警告,防止出现cast异常(类型异常:Ineger和String类型比较就会发生cast异常)
泛型的使用
- 泛型类
public class NormalGeneric<T>{
private T data;
public T getData(){
return data;
}
}
public static void main(String[] args) {
NormalGeneric<String> normalGeneric = new NormalGeneric<>();
normalGeneric.setData("12345");
System.out.println(normalGeneric.getData()); // 输出12345
}
声明了一个泛型类,泛型为T,T类似一个形参,在使用这个类的时候为这个T传入一个实参,类中所有用到的T都替换成实参String,实参必须是一个对象类型;
- 泛型接口:
public interface People<T>{
public T talk();
}
定义了一个People接口,指定了一个泛型T,接口内部有一个talk抽象方法;
实现方式1:泛型类实现泛型接口
class Student<T> implements People<T>{
@Override
public T talk() {
return null;
}
}
实现方式2:普通类实现泛型接口(直接指定泛型的实际类型)
class Teacher implements People<String>{
@Override
public String talk() {
return null;
}
}
- 泛型方法:
public <T> T plus(){
T result = null;
// todo
return result;
}
在使用这个方法的时候指定泛型的实际类型
normalGeneric.<Integer>plus(123);
也可以省去泛型的实际类型,编译器借助传入的参数会自动设置泛型的值
normalGeneric.plus("abc");
泛型变量的限定
public <T extends Comparable> T max(T a,T b){
return a.compareTo(b)>0?a:b;
}
class Man<T extends Comparable>{
T man;
public T getMan() {
man.compareTo(man); // 具有Compareable接口属性
return man;
}
}
如果我们需要泛型变量具有特定接口的属性,可以给泛型T 继承一个接口,用来限定这个泛型的变量必须拥有这个接口的属性,传入的a和b必须实现Compareable接口;
T的extends后面,如果有类必须写在第一个,并且只能有一个类,可以有多个多实现
泛型的局限
- 不能实例化泛型类型:不能new T()
- 静态域不能引用类型变量:应为static静态属性先加载,而泛型的确认是在对象new的时候才知道;
- 必须是包装类型,不能是基础类型
- 不能使用instanceof判断对象类型
- 泛型类不能继承异常
泛型的通配符
类型之间的继承关系不能保证泛型之间的继承关系
下面举一个例子说明这句话的意思
首先声明了如下四个继承关系的类
public class Food {
}
public class Fruit extends Food {
}
public class Apple extends Fruit {
}
public class Orange extends Fruit {
}
再声明一个标准的泛型类
public class GenericType<T> {
private T data;
public void setData(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
我们声明了一个eat方法,传入的参数是泛型为Fruit的标准泛型类,创建了两个标准泛型类,一个泛型是Fruit,一个泛型是Apple,当调用eat方法时,只有fruit有效,apple却不行,可是apple明明继承了fruit,也是水果,为什么不能吃呢??
因为类型的继承不能保证泛型变量之间的继承关系,为了解决吃apple的问题,引入了通配符 ?
我们将泛型的"实参"改成? extends Fruit
,表示接收上界为Fruit的类型(Fruit和Fruit的子类)
下面再理解一句话:通配符用于安全的访问数据
为什么setdata()的时候不管传入fruit还是apple都是不行的,因为通配符? extends Fruit
规定了可能是Fruit,可能是Apple,也可能是Orange,不确定传入的是哪个,所以set的时候都不可以,但是在取出的时候,有一点可以保证,那就是一定是Fruit,(因为Apple和Orange也是Fruit),所以通配符用于安全的访问数据。
通配符只能用在泛型方法上,不可以用在泛型类上
super Apple
表示Apple 和 Apple 的父类:安全的写入数据
super通配符
可以set当前类和其子类,get Object类
extends通配符
不能set , 只能get当前类和其父类
super通配符
可以set当前类和其子类,get Object类
extends通配符
不能set , 只能get当前类和其父类
泛型的原理
类型擦除:(在编译期完成)
在jdk中的泛型是伪泛型,泛型类型在编译的时候会被擦除,jvm看到的只有原始类型ArrayList<String> 和 ArrayList<Object> 在jvm看来是一样的