Effective Java读书笔记六(Java Tips.Day.6)

TIP24消除非受检警告


尽可能的消除每一个unchecked警告。
如果无法消除警告,同时可以证明引起警告的代码是类型安全的,才可以用注解@SuperessWarnings(“unchecked”)注解来禁止这条警告。每当使用这个注解时,一定要写注释说明为什么此处的代码是安全的。


TIP25列表优先于数组


先看下面的代码片段:

Object[] objectArray = new Long[1];
objectArray[0] = "not fit";

List<Object> ol = new LinkedList<Long>(); //此处编译不通过
ol.add("not fit");

数组是协变的(covariant)。相反,泛型是不可变的(invariant)。

如果Sub是Super的子类型,那么Sub[]也是Super[]的子类型。
List<Sub>不是List<Super> 的子类型。

数组是具体化的(reified)

因此,数组会在运行时才会知道并检查元素类型约束。如果将String保存到Long数组中,将会在运行时抛出一个ArrayStoreException的异常。
相比之下,泛型则通过擦除来实现元素类型约束的。泛型在编译时会强化元素类型信息,运行时会擦除元素类型信息。

基于上面的原因,数组与泛型不能很好的混合使用。比如,无法实例化这样的数组: E[] es = new E[1]。同样的, 类似List<String>[] 这样的也无法实例化。它们是不可具体化的。
如果混合使用时,收到了编译时错误或警告,就应当考虑用列表代替数组。


TIP26优先考虑泛型


先来看看一个简单栈的实现

public class DouStack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public DouStack() {
        elements = new Object[DEFAULT_INITIAL_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);
        }
    }
}

OK,我们尝试把它泛型化

public class DStack<E> {
    private E[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public DStack() {
        elements = new E[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(E e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object 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);
        }
    }
}

ok,构造器中的elements = new E[DEFAULT_INITIAL_CAPACITY]; 编译器会报错了。TIP25中也说明了,无法实例化不可具体化的类型。
编写此类用数组支持的泛型时,都可能会遇到此类问题。解决方案有两种:

  1. 创建一个Object数组,并转换成泛型数组。这种用法是合法的,但不是类型安全的,并会收到编译器的未受检的警告。参考24和25条TIPS。

    请注意构造器加上了注解

  2. 将elements的类型从E[]改为Object[],将会得到一个编译错误:

    这里写图片描述

    然后这样: E result = (E) elements[--size];
    这样就没有错误,但多了一条警告:Type safety: Unchecked cast from Object to E

    因为E是不可具体化类型,所以编译器无法在运行时检验转换。如果自己能保证这个转换是安全的,可以加上注解:

    `@SuppressWarnings("unchecked") E result = (E) elements[--size];`
    

    请注意这条注解的位置,是用于声明result的。在使用这条注解时,应该放在最小的需求范围内。

    由于禁止数组类型的未受检转换比禁止标量类型(scalar type)更加危险,建议采用第二种方案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值