-------android培训、java培训、期待与您交流! ----------
Generics
当你从一个集合中拿出一个元素,你要把它转换成以前存入时候的类型。这不但不方便而且这是不安全的。编译器不会检查你所做的转换是当初存入的类型,所以这种类型转换在运行时可能失败。
泛型为你提供了一种告知编译器集合存储元素类型的方法,所以集合的类型就可以被检查了。一旦编译器知道了集合的元素类型,编译器就可以检查你使用集合是否一致,当元素被取出时候也可以插入正确的类型转换。
下面是的例子来自于已经存在的集合教程:
// Removes 4-letter words from c. Elements must be strings
static void expurgate(Collection c) {
for (Iterator i = c.iterator(); i.hasNext(); )
if (((String) i.next()).length() == 4)
i.remove();
}
使用泛型改写的这个例子:
// Removes the 4-letter words from c
static void expurgate(Collection<String> c) {
for (Iterator<String> i = c.iterator(); i.hasNext(); )
if (i.next().length() == 4)
i.remove();
}
当你看到代码“<类型>”时,它读作“什么类型”;上面的声明读作“字符串类型的集合C”。改写后代码使用泛型更加清楚和安全。我们消除了不安全的类型转换和许多额外的圆括弧。更重要的是我们把一部分方法的说明从注释文档移到了它的签名上。所以编译器可以在编译时验证在运行时不会违反类型强转。因为程序编译没有警告所以我们说运行时必然不会抛出ClassCastException异常。使用泛型的净效应尤其是在大型程序中就是提高了可阅读性和稳健性。
Gilad Bracha解释了泛型规范,当我们声明C是String类型的集合时(Collection<String> C)这告诉我们关于变量c的一些事情:无论何时无论在哪里被使用C里面保存的都是String类型,编译器保证了它(假定程序编译时没有警告)。换句话说,泛型就是在代码阶段告诉我们程序员的想法是否正确,Java虚拟机只在运行时才能正真确定程序员的想法是否正确。
虽然泛型的主要使用是集合但是也有些其他的用法。支持类(Holder classes)比如WeakReference和ThreadLocal已经都被泛型化了,也就是说,他们已经被更新以充分利用泛型机制。更惊人的是类Class也已经被泛型化了。类直接量现在就是一种符号,提供了运行时和编译时的双重类型信息。通过新AnnotatedElement接口的getAnnotation方法证实了这使能了一种静态工厂方式:
<T extends Annotation> T getAnnotation(Class<T> annotationType);
这是一个泛型方法。它从它的参数推断类型参数的值,并且返回一个对应的T的实例,就像下面代码片段说明的那样:
Author a = Othello.class.getAnnotation(Author.class);
在有泛型之前你必须强制转换返回的结果到Author。你也没有办法让编译器检查实际的参数类型必须是Annotation的子类型。泛型被实现为类型消除:泛型类型信息只存在于编译时期,在这之后类型信息会被编译器擦除。这种方法的主要优势在于它在泛型代码和以前的原始代码(使用非参数化类型的代码,技术上称之为“原始类型”)之间提供了整体的可互操作性。主要的劣势就是参数类型信息在运行时不可用,当和那些原始的不健壮的代码互操作这种自动产生的转换可能会失败。
java.util.Collections类已经被装备成一个包装类,提供运行时类型安全保证。他们类似于同步和不可修改的包装结构。这些检查集合包装在调试的时候十分有用。假如你有一个String类型的集合S里面有一些老代码神秘的插入了一个整型数,这时自动取出转换到String就会失败,在这个时候检查源代码查找问题已经太晚了。所以如果是这样的话可以替换声明如下:
Set<String> s = new HashSet<String>();
用这个替换:
Set<String> s = Collections.checkedSet(new HashSet<String>(), String.class);
当旧代码尝试插入整型时这个集合将会抛出ClassCastException异常。返回的栈跟踪会指导你诊断修复问题。应该在任何能使用泛型的地方使用泛型。使用泛型库是最直接的,但是需要一些专业知识来写一个泛型库或者泛型化一个已经存在的库。这里有一个警告:如果你打算部署编译好的代码到一个5.0版本之前的Java虚拟机上,你将不能使用泛型(或者任何其他新特性)。
如果你熟悉c++的模板机制,你也许会认为泛型简单,但是相似是很肤浅的。泛型不会为每一个类产生新类,也不允许“模板元编程”。