第二十六条 优先考虑泛型

书中给了个例子

    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 {
        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 extends AbstractQueue ,要求必须是 Delayed 类型,或者 Delayed 的子类型。这是因为这个类的里面实现细节,需要依赖 Delayed 才能实现,这种被称为 有限制的参数类型, 这个随着理解越多,越能感受到通配符的妙用。

你可能感兴趣的:(java,effective,注解,泛型优先,数组)