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

术语:

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

原生态类型(raw type):不带任何实际类型参数的泛型名称。


        每个泛型都存在一个原生态类型,原生态类型就像从类型声名中删除了所有泛型信息一样。考虑如下代码:

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

/**
 * My stamp collection, Contains only Stamp instance.
 */
private final Collection stamps = ...;
        如果不小心将一个coin放进了stamp集合中,编译和运行将照常进行。

// Erroneous insertion of coin into stamp collection
stamps.add(new Coin( ... ));
        在编译和运行时这将不会产生任何错误,但是,一旦从集合中取元素并转换成stamp的时候就会抛出ClassCastException

//Now a raw interator type - don't do this !
for (Iterator i = stamps.iterator(); i.hasNext();) {
	Stamp s = (Stamp) i.next(); // Throws ClassCastException
	... // Do something with the stamp
}
        为了避免这个问题,泛型就显示出了它的威力,将stamps的声名改为如下代码:

// Parameterized collection type - typesafe
private final Collection<Stamp> stamps = ...;
        这段代码告诉编译器,stamps只应该包括Stamp的实例,如果错误的插入一个元素,那么这将会产生一条编译时的错误消息,准确的报告错误的位置。还有一个潜在的好处是从集合中取元素时不需要再进行手动的转换了,因为编译器知道它应该属于哪种类型,编译器会直接完全这个转换。

        使用原生态类型虽然也是合法的,但是不应该这样做,因为这会失去泛型在安全性和表述性方面的所有优势。原生态类型存在的理由是因为老代码的移植兼容性。

        原生态的List和参数化的类型List<Object>之间有什么区别呢?不严格的讲,前者逃避了泛型检查(因为他并不属于泛型系统),后者明确告诉编译器,它能够持有任意类型的对象。虽然可以将List<String>传递给类型为List的参数,但是不能将它传给类型为List<Object>的参数。泛型有子类型化的规则,List<String>是原生态类型List的一个子类型,但是它不是参数化类型List<Object>的子类型。因此如果使用像List这样的原生态类型,就会失去类型的安全性,但是如果使用像List<Object>这样的参数化类型则不会。看如下例子:

// Use raw type (List) - fails at runtime!
public static void main(String[] args) {
	List<String> strings = new ArrayList<String>();
	unsafeAdd(strings, new Integer(42));
	String s = strings.get(0); // Compiler - generated cast
}

private static void unsafeAdd(List list, Object o) {
	list.add(o);
}
        在这里,main函数中使用泛型的初衷是好的,但是在调用unsafeAdd的时候却使用了原生态类型,这样就失去了泛型的类型检查机制,将非String类型的元素添加到了集合里,所以,将使用get方法取出这个错误元素将将其赋给String类型的时候编译出错了,这个错误是可以避免的, 不要使用原生态类型做为参数类型

        原生类型是很危险的,但是在某些情况下不确定或者不在乎集合中的元素类型是什么,这个时候可能使用原生类型更方便一些。Java提供了在这种情况下安全替换的方法,称为无限制的通配符类型。如果要在泛型,但不确定或者不关心实际类型参数,那就可以使用一个问号来代替。带有问号的set<?>是最普通的参数化Set类型,可以持有任何集合,也就是说他是任何Set泛型的父类,但是除了往其中添加null以外什么元素也添加不了,因为编译器不知道?代表的到底是什么类型,所以根据泛型安全规则不允许将某具体元素插入其中,为什么null可以?因为null是任何类型的成员(具体见《Java泛型指南》)。如果觉得这个限制过于严厉了一些,那就可以考虑使用泛型方法或者有限制的通配符类型。

        不要在新代码中使用原生态类型,这条规则有两个例外:1、由于泛型信息可以在运行时被擦除(JVM不知道泛型系统的存在),因此在参数化类型而非无限制通配符上使用instanceof操作是非法的。用无限制通配符类型代替原生太类型,对instanceof操作符的行为又不会产生任何影响(因为?代表的任意一种类型,但是是确定的)。这种情况下使用原生类型做成了首选,下面是利用泛型来使用instanceof操作符的首选方法:

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

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

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

        总之,使用原生态类型可能会在运行时导致异常,因此不要在新代码中使用。记住以下三种情况的区别:1、Set<Object>是个参数化类型,表示可以包含任何对象类型的一个集合。2、Set<?>是一个通配符类型,表示只能包含某种未知对象类型的一个集合。3、Set是一个原生态类型,它脱离了泛型系统。前两种是安全的,最后一种是不安全的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值