术语
参数化的类型:List<String> 实际类型参数:String 泛型:List<E> 形式类型参数:E 无限通配符类型:List<?> 原生态类型:List 有限制类型参数:<E extends Number> 递归类型限制:<T extends Comparable<T>> 有限制通配符类型:List<? extends Number> 泛型方法:static<E> List<E> asList(E[] a) 类型方法:String.class
不要使用原生态类型
泛型:声明中具有一个或多个类型参数的类 或接口
原生态类型会失去泛型在安全性和描述性方面的优势
消除非受检的警告,不能忽视
列表优于数组
优先考虑泛型
只要时间允许,就要把现有的类型都泛型化
泛型化类例子
package codeTemplate.effectiveJava.bean;
import java.util.ArrayList;
import java.util.List;
public class Stack<E> {
private List<E> elements;
private int size;
public Stack() {
elements = new ArrayList<>();
}
public void add(E e) {
elements.add(e);
size++;
}
public E pop() {
size--;
return elements.remove(0);
}
public static void main(String[] args) {
Stack<String> stringStack = new Stack<>();
stringStack.add("1");
String element = stringStack.pop();
System.out.println(element);
}
}
优先考虑泛型方法
泛型方法的定义
修饰符 <T,E,…> 返回值类型 方法名(形参列表){ 。。。。。。 } 修饰符与返回值类型中间的 泛型标识符 <T,E,…>,是 泛型方法的标志,只有这种格式声明的方法才是泛型方法。 泛型方法声明时的 泛型标识符 <T,E,…> 表示在方法可以使用声明的泛型类型。 与泛型类相同,泛型标识符可以是任意类型,常见的如T,E,K,V 等。 泛型方法可以声明为 static 的,并且与普通的静态方法是一样的。
public static <E> E checkNullAndReturn(E o) {
if (o == null) {
throw new NullPointerException();
}
return o;
}
//使用
String o = Stack.checkNullAndReturn("123");
利用有限通配符来提升API的灵活性
参数化类型是不变的,如List不是List的子类,List只能放String类型,不能转化成List
public void addAll(List<? extends E> list) {
elements.addAll(list);
}
使用
//E为Object
Stack<Object> stringStack = new Stack<>();
//可以接收String(Object的子类)
stringStack.add("1");
Object element = stringStack.pop();
System.out.println(element);
//可以接收List<Integer>(符合List<? extends Object>)
stringStack.addAll(Arrays.asList(1,2));
注:
- 为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符类型
- PECS:producer-extends,consumer-super
- 所有的comparable和comparator都是消费者,Comparable<? super T>,Comparator<? super T>
谨慎并用泛型和可变参数
可变参数和泛型不能良好地合作
优先考虑类型安全的异构容器
常见泛型
Set Map<K,V>,ThreadLocal,AtomicReference,这些都是参数化了的容器,每个容器只能有固定数目的类型参数,可以将类型参数放在键上而不是容器上来避开这一限制。类型安全的异构容器,可以用Class对象作为键,如Map<Class<?>, Object>,其中
Class<?>是Class的无线通配符,表示可以是任意Class类型
类型令牌:当一个类的字面被用在方法中,来传达编译时和运行时的类型信息
@Getter
private Map<Class<?>, Object> favorites = new HashMap<>();
public <E> void putFavorite(Class<E> type, E instance) {
favorites.put(type, instance);
}
stringStack.putFavorite(String.class,"123");
System.out.println(stringStack.getFavorites());
//结果
{class java.lang.String=123}