书中给了个例子
class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if(size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}
public boolean isEmpty() {
return size == 0;
}
private void ensureCapacity() {
if(elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
这个例子是自定义个栈,底层用Object类型数组来装数据,为了提高效率和安全性,应该使用泛型,那么,直接修改,会变成
class Stack<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITAL_CAPACITY = 16;
public Stack() {
elements = new E[DEFAULT_INITAL_CAPACITY];
}
public void push(E e) {
ensureCapacity();
elements[size++] = e;
}
public E pop() {
if(size == 0) {
throw new EmptyStackException();
}
E result = elements[--size];
elements[size] = null;
return result;
}
public boolean isEmpty() {
return size == 0;
}
private void ensureCapacity() {
if(elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
泛型是有了,但问题来了。 上一章我们分析过,泛型对象不能直接被创建,elements = new E[DEFAULT_INITAL_CAPACITY]; 这行代码会报错。但是我们还需要用泛型来增加安全性,怎么办?两种解决方法
第一种是从创建数组对象上解决,既然不能直接创建泛型对象,那么创建实体对象,然后转换为泛型对象,示例 elements = (E[]) new Object[DEFAULT_INITAL_CAPACITY];这样就可以了。但是还是产生了一条警告,因为类型是安全的,我们可以用SupressWarning注释忽略掉该警告。
第二种是 把 private E[] elements; 直接变换为 private Object[] elements; 不用泛型数组,用Object数组,此时, pop()方法会报错,那么,直接修改 E result = elements[--size]; 这行代码,修改为 E result = (E) elements[--size]; 直接把 Object 对象转换为 E 的对象类型。与第一种方法一样,这也会收到一条警告,同样,用注解忽略该警告。
以上两种方法都可以,但一般来说,方法二的消除警告范围小,数组类型的不受检查比单独一个对象的不受检查的危险大,因为范围不一样,所以用方案二的比较多,例如 ArrayList 源码,采用的就是方案二。 但如果说,创建一个Stack,不停的增加和删除元素,方案二中需要不停的把Object转换为E,方案一只需要创建数组时把数组转换为E[],只转换一次,开销比着方案二要节省,所以有时候自定义的话,可能更多的人会选择方案一。
上一章,我们说列表是优先于数组的,但实际上java不知直接支持列表,我们所用的列表 ArrayList LinkedList 都是二次封装的,尤其是 ArrayList 是对数组进行封装,使用泛型技术,基本排除了危险,保证使用的安全,可以放心的使用。HashMap 也是用一些 数组+链表 的格式,来产生高效的集合供大家使用。 上一章和这一章的意思都差不多,列表是源码中封装好的,可以放心使用,直接使用数组,一旦使用不当,很容易出错;如果对数组和泛型及其他掌握比较精通,那我们可以用数组写出自己的 ArrayList 等集合,自己尽情发挥,彰显成就感。总之,泛型好处多多,在我们自定义集合时,加上泛型更是如虎添翼。
一些特殊的类,对传入的对象类型有一定的限制,比如这个,class DelayQueue<E extends Delayed> extends AbstractQueue<E> ,要求必须是 Delayed 类型,或者 Delayed 的子类型。这是因为这个类的里面实现细节,需要依赖 Delayed 才能实现,这种被称为 有限制的参数类型, 这个随着理解越多,越能感受到通配符的妙用。