建议:请不要在新代码中使用原生态类型。

声明中具有一个或者多个类型参数(type parameter)的类或者接口,就是泛型(generic)类或者接口。例如,从Java1.5发行版本起,List接口就只有单个类型参数E,表示列表的元素类型。从技术的角度来看,这个接口的名称应该是指现在的LIst<E>(读作“E的列表”),但是人们经常把它简称为List。泛型类和接口统称为泛型(generic type)。

每种泛型定义一组参数化的类型(parameterized type),构成格式为:先是类或者接口的名称,接着用尖括号(<>)把对应于泛型形式类型参数的实际类型参数列表括起来。例如,List<String>(读作“字符串列表”)是一个参数化的类型,表示元素类型为String的列表。(String是与形式类型参数E相对应的实际类型参数。)

最后一点,每个泛型都定义一个原生态类型(raw type),即不带任何实际类型参数的泛型名称。例如,与List<E>相对应的原生态类型是LIst。原生态类型就像从类型声明中删除了所有泛型信息一样。实际上,原生态类型List与Java平台没有泛型之前的接口类型List完全一样。

有了泛型,就可以利用改进后的类型声明来代替集合中的这种注释,告诉编译器之前的解释中所隐含的信息:

// Parameterized collection type -typesafe

private final Collection<Stamp> stamps = ...;

还有一个好处是,从集合中删除元素时不再需要进行手工转换了。编译器会替你插入隐式的转换,并确保他们不会失败(依然假设所有代码都是通过支持泛型的编译器进行性编译的,并且没有产生或者禁止任何警告)。无论你是否使用for-each循环,上述功能都能适用。

如果使用原生态类型,就失掉了泛型在安全性和表述性方面的所有优势。既然不应该使用原生态类型,为什么Java的设计者还允许使用他们呢?这是为了提供兼容性。因为泛型出现的时候,Java平台即将进入他的第二个10年,已经存在大量没有使用泛型的Java代码。人们认为让所有这些代码保持合法,并且能够与使用泛型的新代码互用,这一点很重要。他必须合法,才能将参数化类型的实例传递给那些被设计成使用普通类型的方法,反之亦然。这种需求被称作移植兼容性(Migration Compatibility),促成了支持原生态类型的决定。

虽然不应该在新代码中使用像List这样的原生态类型,使用参数化的类型以允许插入任意对象,如List<Object>,这还是可以的。原生态类型List和参数化的类型LIst<Object>之间到底有什么区别呢?不严格的说,前者逃避了泛型检查,后者则明确告知编译器,他能够持有任意类型的对象。虽然你可以将LIst<String>传递给类型List的参数,但是不能讲它传给类型LIst<Object>的参数。泛型有子类型化(subtyping)的规则,List<String>是原生类型List的一个子类型,而不是参数化类型List<Object>的子类型。因此。如果使用像LIst这样的原生态类型,就会失掉类型安全性,但是如果使用像List<Object>这样的参数化类型,则不会。

从Java 1.5发行版本开始,Java就提供了一种安全的替代方法,称作无限制的通配符类型(unbounded wildcard type)。如果要使用泛型,但不确定或者不关心实际的类型参数,就可以使用一个问号代替。例如,泛型Set<E>的无限制通配符类型为SEt<?>(读作“某个类型的集合”)。这是最普通的参数化Set类型,可以持有任何集合。

在无限制通配符类型Set<?>和原生态类型Set之间有什么区别呢?这个问号真正起到作用了吗?这一点不需要赘述,但通配符类型是安全的,原生态类型则不安全。由于可以将任何元素放进使用原生态类型的集合中,因此很容易破坏该集合的类型约束条件;但不能将任何元素(除lenull之外)放到Collection<?>中。

你不仅无法将任何元素(除了Null之外)放进Collection<?>中,而且根本无法猜测你会得到哪种类型的对象。要是无法接受这些限制,就可以使用泛型方法(generic method)或者有限制的通配符类型(bounded wildcard type)。

不要在新代码中使用原生态类型,这条规则有两个小小的例外,两者都源于“泛型信息可以在运行时被擦除”这一事实。在类文字(class literal)中必须使用原生态类型。规范不允许使用参数化类型(虽然允许数组类型和基本类型)。换句话说,List.class,String[].class和int.class都合法,但是LIst<String.class>和LIst<?>.class则不合法。

这条规则的第二个例外与instanceof操作符有关。由于泛型信息可以在运行时被擦除,因为在参数化类型而非无限制通配符类型上使用instanceof操作符是非法的。用无限制通配符类型代替原生态类型,对instanceof操作符的行为不会产生任何影响。在这种情况下,尖括号(<>)和问号(?)就显得多余了。下面是利用泛型来使用instanceof操作符的首选方法:

// Legitimate use of raw type - instanceof operator

if (o instanceof Set) { // Raw type

Set<?> m  = (Set<?>)o;  // Wildcard

}

注意,一点确定这个o是个Set,就必须将它装换成通配符类型Set<?>,而不是转换成原生态类型Set。这是个受检的(check)转换,因此不会导致编译时警告。

总之,使用原生态类型会在运行时导致异常,因此不要在新代码中使用。原生态类型只是为了与引入泛型之前的遗留代码进行兼容和互用而提供的。让我们做个快速的回顾:Set<Object>是个参数化类型,表示可以包含任何对象类型的一个集合;Set<?>则是一个通配符烈性,表示只能包含某种未知对象类型的一个集合;Set则是个原生态类型,她脱离了泛型系统。前两种是安全的,最后一种不安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值