Effective Java读书笔记——第五章 泛型

JDK1.5引入了泛型,在泛型引入之前,从集合中读取到的每个对象都必须进行转换,如果不小心引入了错误的对象,在运行时就会出错。有了泛型之后,编译器会在编译阶段检查你是否插入了错误的对象。这样是程序更加安全。


第23条:请不要在新代码中使用原生态类型

所谓的原生态类型,就是不太任何实际类型参数的泛型名称,例如List<E>相对应的原生态类型就是List。

本条说的主要是使用通配符的要点,可以参考这几篇文:

泛型之中的通配符(Wildcards)使用

Java泛型– 通配符

Java 泛型通配符?解惑


第24条:消除非受检警告

消除编译期出现的警告,可以避免的运行时出现ClassCastException异常。

如果无法消除警告。同时可以证明引起警告的代码安全的,才可以使用一个@SuppressWarning("unchecked")注解来禁止这条警告。

@SuppressWarning("unchecked")注解警告的代码范围应该尽可能的小,不要在整个类上加这个注解。

第25条:列表优先于数组

列表和数组的两个主要不同点:

1、数组是协变的(即如果Sub是Super的子类型,那么数组类型Sub[]就是Super[]的子类型),而泛型是不可变的(即对于任意两个不同的类型Type1和Type2,List<Type1>既不是List<Type2>的子类型,也不是List<Type2>的超类型)。

看个栗子:

Object[] objectArray = new Long[1];
objectArray[0] = "I don't fit in";

上面程序会在编译时通过,但运行时失败。而下面的代码会在编译时失败:

List<Object> o1 = new ArrayList<Long>();//编译失败
o1.add("I don't fit in");

第二个区别是:数组是具体化的,因此数组在运行时才知道并检查它们的元素类型约束。而泛型只在编译时强化它们的类型信息。

正是由于这些区别,导致数组和泛型不能很好混用,下面这些都是非法操作:

//非法
new List<E>[]
new List<String>[]
new E[] 

为什么泛型数组会出错,下面通过示例说明:

//创建了一个泛型数组,这是非法的
List<String>[] stringLists = new List<String>[1];//(1)

//创建并初始化了一个包含单个元素的List<Integer>,合法
List<Integer> intList = Arrays.asList(42);//(2)

//将List<Stirng>数组保存到一个Object数组变量中,这是合法的
Object[] objects = stringLists;//(3)

//将List<String>保存到Object数组的位移元素中,合法
objects[0] = intList;//(4)
string s = stringLists[0].get(0);//(5)

我们将一个List<Integer>实例保存到了原本声明只包含List<String>实例的数组中。在(5)中我们从这个数组中获取列表的唯一元素,编译器自动地将获取到的元素转换成String,但它是个Integer。于是抛出一个ClassCastException异常,所以在第一行会产生一个编译错误。

总之,数组和泛型游着非常不同的类型规则。数组是协变且可以具体化的;但是泛型是不可协变的且可以被排除的。因此,数组提供了运行时的类型安全,但是没有编译时的类型安全,反之,对于与泛型也一样。一般来说,数组和泛型不能很好地混合使用。如果你发现自己将它们混合起来使用,并且得到了编译时错误或者警告,你的第一反应该是用列表代替数组。

第26条:优先考虑泛型

这是第6条中的堆栈实现:

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    public Stack(){
        elements = new Objects[DEFAULT_INITIAL_CAPACITY];
    }
    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }
    public Object pop() {
        if(size = 0) {
            throw new EmptyStackException();
        }
        return elements[--size];
    }
    private void ensureCapacity() {
        if(elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }


    }



}

可以强化这个类用泛型实现:

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

    public Stack {
    //这句话报错
        elements = new E[DEFAULT_INITIAL_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;
    }
}

在上面这个类中,elements = new E[DEFAULT_INITIAL_CAPACITY];这句话会报错。因为不能创建不可具体化的类型数组。如E。

解决方法一:创建一个Object[]数组,强转为E[]:

elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];

这会产生一个警告(类型转化的警告),但是我们确信这是合法的,所以可以加注解消除警告:

@SupressWarning("uncheked")
Stack {
    ...

}

第二种解决方式:将E[] 改为Object[],并进行强转:

E result = (E) elemments[--size];

最后示范泛型Stack类的使用。程序以相反的顺序打印出它的命令行参数,并转换成大写字母:

public static void main(String[] args) {
    Stack<String> stack = new Stack<String>();
    for(String arg : args) {
        stack.push(args);
    }
    while(!stack.isEmpty()) {
        System.out.println(stack.pop().toUpperCase());
    }
}

使用泛型比使用需要在客户端代码中进行转换的类型来的更安全,也更容易,在设计新类的时候,要确保它们不需要这种转换就可以使用。这通常意味着要把类做成泛型的。

第27条:有限考虑泛型方法

下面这个方法,返回两个集合的联合:

public static Set union(Set s1,Set s2) {
    Set result = new HashSet(s1);
    result.addAll(s2);
    return result;
}

这会出现警告。为了修正这些警告:

public static <E> Set<E> union(Set<E> s1,Set<E> s2) {
    Set<E> result = new HashSet<E>(s1);
    result.addAll(s2);
    return result;
}

使用上面的方法:

public static void main(String[] args) {
    Set<String> guys = new HashSet<String>(Arrays.asList("Tom", "Dick", "Harry"));
    Set<String> stooges= new HashSet<String>(Arrays.asList("Larry", "Moe", "Curly"));
}
Set<String> aflcio = union(guy, stooges);
Systen.out.println(aflcio);

泛型方法像泛型一样,使用起来比要求客户端转换输入参数并返回值的方法来的更加安全,也更加容易,在设计泛型方法时,应确保新方法可以不用转换就能使用。

第28条:利用有限制通配符来提升API的灵活性

第29条:优先考虑类型安全的异构容器

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值