Effective Java:泛型篇

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的灵活性

通配符:?extendssuper

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 是错误的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值