目录
Item 27: Eliminate unchecked warnings
Item 28: Prefer lists to arrasy
Item 30: Favor generic methods
Item 31: Use bounded wildcards to increase API flexibility
Item 33: Consider typesafe heterogeneous containers
Item 26: Don't use raw types
不要使用原生态类型
泛型包含泛型类和泛型接口。
原生态类型指List这种没有指定具体类型参数的泛型,如List<E>的raw type是List、Collection<E>的是Collection、Iterator<E>的是Iterator等等。
原生类型raw type指不带任何实际类型参数的泛型名称,它们的存在是为了兼容泛型出现之前的代码。
如果使用原生态类型,会失掉泛型在安全性和描述性方面的优势。
不能将任何元素(除null外)放到无限制通配类型(如Collections<?>)中,如下:
// 错误代码
void test(Set<?> s) {
s.add("e"); // 会报错
}
必须在类字面量(class literal)中使用原生态类型。List.class、String[].class、int.class合法,但List<String>.class、List<?>.class不合法。
泛型相关术语表
术语 | 示例 | 参见条目 |
---|---|---|
参数化的类型 parameterized type | List<String> | Item 26 |
实际类型参数 actual type parameter | String | Item 26 |
泛型 generic type | List<E> | Item 26, 29 |
形式类型参数 formal type parameter | E | Item 26 |
无限制通配符类型 unbounded wildcard type | List<?> | Item 26 |
原生态类型 raw type | List | Item 26 |
有限制类型参数 bounded type parameter | <E extends Number> | Item 29 |
递归类型限制 recursive type bound | <T extends Comparable<T>> | Item 30 |
有限制通配符类型 Bounded wildcard type | List<? extends Number> | Item 31 |
泛型方法 Generic method | static <E> List<E> asList(E[] a) | Item 30 |
类型令牌 Type token | String.class | Item 33 |
Item 27: Eliminate unchecked warnings
消除非受检的警告
要尽可能消除每一个非受检警告。如果无法消除,但可以证明是类型安全的,可以用注解@SuppressWarnings("unchecked")来禁止这类警告,同时添加注释说明原因。
应该在尽可能小的范围使用@SuppressWarnings。
Item 28: Prefer lists to arrasy
列表优先于数组
数组array是协变的(covariant):如果Sub为Super的子类,那么数组类型Sub[]也是Super[]的子类。
泛型是不可变的(invariant):任意两个不同类型Type1、Type2,即使它们存在继承关系,List<Type1>和List<Tpye2>都不存在超-子(父-子)类型关系。
数组是可以具体化的reified,即在编译时和运行时都知道它们的元素类型。
泛型是通过擦除(erasure)来实现的,不可具体化的(non-reifiable),即只有在编译时才知道它们的元素类型,在运行时丢弃(或擦除)元素的类型信息。擦除主要是为了兼容历史代码。
因此,创建泛型数组是非法的,如new List<E>[]、new E[]、new List<String>[]都会出现“generic array creation errors”。
Item 29: Favor generic types
优先考虑泛型
示例:
public class Stack<E> {
private E[] elements;
public Stack() {
elements = new E[16];
}
...
}
Item 30: Favor generic methods
优先考虑泛型方法
示例:
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
Set<E> result = new HashSet<>(s1);
result.addAll(s2);
return result;
}
Item 31: Use bounded wildcards to increase API flexibility
利用有限制通配符来提升API的灵活性
有限制通配符有2种:<? extends E>和<? super E>。
使用口诀:PECS,即producer-extends、comsumer-super。
示例:
// producer-extends情况
public void pushAll(Collection<? extends E> src) {
for(E e : src) {
push(e); // 泛型参数变量作为生产者,给外界提供数据
}
}
// consumer-super情况
public void popAll(Collection<? super E> dst) {
while(!isEmpty()) {
dst.add(pop()); // 泛型参数变量作为消费者,消费外界数据
}
}
Item 32: Combine generics and varargs judiciously
谨慎并用泛型和可变参数
Item 33: Consider typesafe heterogeneous containers
优先考虑类型安全的异构容器
泛型的一般用法有个限制,即每个容器只能有固定数量的类型参数,而异构容器通过将类型参数放到key值而非容器上绕开此限制,如:
// 类型安全的异构容器模式
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();
public <T> void putFavorite(Class<T> type, T instance) {
favorites.put(Objects.reguireNonNull(type), instance);
}
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
}
}
// 客户端代码
Favorites f = new Favorites();
f.putFavorite(String.class, "Java");
f.putFavorite(Integer.class, 0xcafebabe);
f.putFavorite(Class.class, Favorites.class);
Class<?> favoriteClass = f.getFavorite(Favorites.class);