23 不要在新代码中使用原生态类型
比如List<Object>
不要使用成 List
这个原生态类型,这样子,同一List,什么数据都能往里面加,当取出数据的时候,就会容易出现由于不确定类型而导致的出错。
使用泛型的时候,如果不确定类型,可以使用这个样子:List<?>
,?
表示不确定类型,但是当加入了一种类型的数据之后,那么这个List就只能存这种数据了。
24 消除非受检警告
使用泛型编程的时候,会遇到许多编译器警告:非受检强制转化警告(unchecked cast warnigs)、非受检方法调用警告、非受检普通数组创建警告,以及非受检装换警告(uncheked conversion warnings)。
编写无警告程序,如果出现警告,在确定该处无错误,那么可以使用@SuppressWarnings("unchecked")
25 列表优先于数据
数组和泛型相比,有两个不同点。
第一个不同点
数组是协变的。协变(covariant):如果Sub为Super的子类型,那么数组Sub[]就是Super[]的子类型(???)相反,泛型是不可变的,既不是谁谁的子类型,声明的时候是什么就是什么。
下面是合法的代码片段:
//Array
Object[] objectArray = new Long[1];
ojbectArray[0] = "Hello"; // Runtime Error
List<Object> ol = new ArrayList<Long>();
ol.add("Heelo"); //Compile Error
第一种方法:运行时发现错误
第二种方法:编译时发生错误
第二个不同点
数组是具体化的,在运行时才知道检查他们的元素类型约束。
泛型通过擦除来实现的:在编译时强化它们的类型信息,容易发现错误。
总之: 数组提供运行时的类型安全,但是没有编译时的类型安全。泛型则是相反。
26 优先考虑泛型
使用泛型比使用需要在客户端代码进行转换的类型来的更加安全,也更加容易(比如在栈pop对象的时候,不用客户端对结果进行转换)。在设计新类型的时候,确保他们不需要这种转换就可以使用。
以Stack的设计为例子:
// 非泛型设计
public class Stack{
private Object[] elements;
...
public Stack(){
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
//这种设计,客户端要自己转化类型
public Object pop(){
...
Object result = elements[--size];
elements[size] = null;
return result;
}
}
如果以单纯的泛型设计:
public class Stack<E>{
private E[] elements;
...
public Stack(){
elements = new E[DEFAULT_INITIAL_CAPACITY];
}
public E pop(){
...
E result = elements[size--];
...
}
}
在创建Elements的时候,会出错,因为不运行创建不确定类型的数组new E[]
;
解决方法:
1.
@SuppressWaringings("unchecked") //确定无误,消除转换警告
public Stack(){
elements = (E[])new Object[DEFAULT_INITIAL_CAPACITY];
}
2.
public E pop(){
@SuppressWaringings("unchecked") E result = (E)elements[size--];//elements为Objectp[]
}
第一种禁止数组类型的未受检转换比禁止标量类型的更加危险,但是不用多处转换。
27 优先使用泛型方法
泛型方法就像泛型一样,使用起来比要求客户端转换输入参数并返回值的方法来得更加安全。
28 利用有限制通配符来提升API的灵活性
通配符:?
、 extends
、 super
extends
简单的例子:
public void pushAll(Iterable<? extends E> src){...}
这个方法中,支持类型E的任何子类型。
super
public void popAll(Collection<? super E> dst){...}
这个方法中,支持任何E的超类。
使用两种的场合:
PECS :producer-extends, consumer-super
增加一个东西的时候,使用extends,如向栈中添加一个东西;
获取一个东西的时候,使用Super ,如从栈中获取一个东西;
29 优先考虑类型安全的异构容器
异构的(heterogeneous):不像普通的map,它的所有键都是不同类型的。
一个实现:
publ;ic class Favorites{
private Map<Class<?>, Object> favorites =
new HanshMap<Class<?>,Object>();
public <T> void putFavorite(Class<T> type, T instance){
if(type == null)
throw new NullPointerException("Type is null");
favorites.put(type, instance);
}
public <T> T getFavorite(Class<T> type){
return type.cast(favorites.get(type)); //注意这里的实现
}
}
Class<?>
是嵌套的,它不属于通配符类型的Map的类型,而是它的键的类型,所以这个Map的每个键可以有一个不同的参数化类型。
由于favorites Map的值是Object类型,没有和键有必然的联系。在获取的时候,需要返回一个 t
,利用了Class的cast
方法,将对象动态的转换长了Class对象所表示的类型。这个方法只是检查是否为Class对象所表示的类型,如果不是,就会抛出异常。
Favorite类的局限性: 它不能保存List<String>
这种类型,以为List<String>.class
是错误的。