一. 概述
1. 什么是泛型?
泛型:将还未知的类型参数化(类型形参)并用大写字母作为临时标识,然后在调用时再传入具体的类型(类型实参)。泛型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
2. 为什么引入泛型?
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,这个时候元素的类型不确定,因此把元素的类型设计成一个类型参数:泛型(JDK1.5引入)。Collection< E >,List< E >,ArrayList< E > 这个 < E > 就是类型参数,即泛型。
借一个栗子:
//未用泛型
List arrayList = new ArrayList();
arrayList.add("abc");
arrayList.add(123);
for(int i = 0; i < arrayList.size(); i++){
String item = (String)arrayList.get(i);
System.out.println(item);
} //java.lang.ClassCastException:java.lang.Integer cannot be cast to java.lang.String
-------------------------------------------------------------------------------------
//加入泛型
List<String> arrayList = new ArrayList();
arrayList.add("abc");
arrayList.add(123); //在编译阶段会报错
有了泛型,解决了获取数据元素时,需要类型强制转换的问题,同时保证了如果程序在编译时没有发岀警告,运行时就不会产生 ClassCastException 异常。
二. 使用泛型
1. 泛型类
泛型类的类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开,典型的就是各种容器类,如HashMap<K, V>
自定义泛型类
class Order<T>{
private String name;
private int id;
private T orderT;
public Order(){}
public Order(String name; int id; T orderT){
this.name = name;
this.id = id;
this.orderT = orderT;
}
//在方法中使用了泛型,但是这并不是一个泛型方法,是用声明泛型类已经声明过的泛型作为返回类型
public T getOrderT(){
return orderT;
}
public void setOrderT(T orderT){
this.orderT = orderT;
}
}
@Test
//实例化时指明类的类型,否则默认为 Object 类型
public void test(){
Order<String> order1 = new Order<>();
Order<String> order2 = new Order<>("TOM", 16, "Male");
.
.
}
2. 泛型接口
泛型接口与泛型类的定义及使用基本相同,泛型接口常被用在各种类的生产器中
//定义一个泛型接口
public interface Generator<T>{
public T next();
}
3. 泛型方法
- 泛型类,是在实例化类的时候指明泛型的具体类型
- 泛型方法,是在调用方法的时候指明泛型的具体类型
泛型方法格式:访问权限 <泛型> 返回类型 方法名(泛型标识 参数名称) 抛出的异常
//方法里的泛型参数与类的泛型参数没有任何关系
public <E> List<E> copyFromArryToList(E[] arr) {
ArrayList<E> list = new ArrayList<>();
for (E e : list){
list.add(e);
}
return list;
}
无论何时,如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,那么就应该使用泛型方法。static 方法无法访问类上定义的泛型,如果 static 方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
4. 泛型继承的原则
假设 A 是 B 的父类
C< A > 和 C< B > 是并列关系,不具备父子类关系
A< C > 是 B< C > 的父类
5. 通配符
使用 ? 代替具体的类型参数,但 ? 是类型实参,而不是类型形参。
例如 List<?> 在逻辑上是 List< String >,List< Integer > 等所有List<具体类型实参>的父类
通配符的错误用法
//注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
public static <?> void test(ArrayList<?> list){}
//注意点2:编译错误:不能用在泛型类的声明上
class GenericTypeClass<?>{}
//注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象
ArrayList<> list2 new ArrayList<?>();
受限泛型
- 通配符指定上限:类名称<? extends 类> 对象名称
- 通配符指定下限:类名称<? super 类> 对象名称