【笔记26】不要使用原始类型

泛型(generic type):声明中具有一个或者多个类型参数的类或者接口的统称。

原始类型(raw type):没有任何类型参数的泛型类型的名称。如List对应的原生态类型是List。它们的存在主要是为了与没有泛型之前的代码相兼容。

出现泛型前:

// Now a raw collection type - don't do this!

//这个集合里面应该放stamp类的实例
private final Collection stamps = ...;
//如果把Coin类的实例放进去了,编译和运行照常进行
stamps.add(new Coin());

//直到从stamps集合中获取将coin取出来,却误以为它是stamp才会出错
for(Iterator i = stamps.iterator(); i.hasNext(); ) {
    Stamp s = (Stamp) i.next();
}

有了泛型之后:

private final Collection s = ...;

//编译的时候,这句代码就会出错
s.add(new Coin());

List和List的区别:前者逃避泛型检查,后者明确告诉编译器,它能持有任意类型的对象。虽然可以将List传递给类型List的参数,但不能传递给类型List的参数。因为List不是List的子类型。

无限制通配类型Set和Set的区别:如果不确定或者不关心实际的类型参数,就使用无限制通配类型,不能将任何元素(除null外)放到Set中,如果尝试这样做,在编译时就会提示错误。

不要在新代码中使用原生态类型,除非两个例外【两者都源于泛型信息可以在运行时被擦除(JVM不知道泛型系统的存在)】:

1、在类文字中必须使用原生类型。规范不允许使用参数化类型(虽然允许数组类型和基本类型)。换句话说,List.class,String[].class,int.class都是允许的,但就List.class和List.class则不合法。

2、由于泛型信息可以在运行时被擦除,因此在参数化类型而非无限制通配符上使用instanceof操作是非法的。用无限制通配符类型代替原生态类型,对instanceof操作符的行为又不会产生任何影响(因为?代表的任意一种类型,但是是确定的)。这种情况下使用原生类型做成了首选,下面是利用泛型来使用instanceof操作符的首选方法:

// Legitimate use of type - instanceof operator
if (o instanceof Set) { // Raw type
    Set m = (Set) o; // Wildcard type
}

        上面的代码做了什么?首先确定了o是一种Set,但是是哪一种并不知道,这样直接将其转换成无限制通配符类型Set,这是受检的转换,不会导致编译时警告。

 总结:

        总之,使用原始类型可能导致运行时异常,所以不要使用它们

        记住以下三种情况的区别:

1、Set是个参数化类型,表示可以包含任何对象类型的一个集合。

2、Set是一个通配符类型,表示只能包含某种未知对象类型的一个集合。

3、Set是一个原生态类型,它脱离了泛型系统。前两种是安全的,最后一种是不安全的。

为了快速参考,下表中总结了本条目(以及本章稍后介绍的一些)中介绍的术语:

术语 中文含义 举例 所在条目
Parameterized type 参数化类型 List 条目 26
Actual type parameter 实际类型参数 String 条目 26
Generic type 泛型类型 List 条目 26
Formal type parameter 形式类型参数 E 条目 26
Unbounded wildcard type 无限制通配符类型 List 条目 26
Raw type 原始类型 List 条目 26
Bounded type parameter 限制类型参数 条目 29
Recursive type bound 递归类型限制 > 条目 30
Bounded wildcard type 限制通配符类型 List 条目 31
Generic method 泛型方法 static List asList(E[] a) 条目 30
Type token 类型令牌 String.class 条目 33

你可能感兴趣的:(Effective,Java,Effective,Java)