Java泛型总结(八)

2014-11-24 03:08:01 · 作者: · 浏览: 19
例。为了具体的说明这点,下面的例子提供了不使用get(0)方法来获取一个List的第一个元素的方法。
List words = // ...initialized elsewhere...
Iterator iterator = words.iterator();
String firstword = iterator.next();
10. 理解泛型类型
本段将对泛型类型的使用细节做进一步的探讨,以尝试说明下列问题:
不带类型参数的使用泛型的后果
参数化类型的体系
一个关于编译期泛型类型的类型安全的漏洞和一个用于确保运行期类型安全的补丁
为什么参数化类型的数组不是类型安全的
未经处理的类型和不被检查的警告
即使被重写的Java集合类带来了泛型的好处,在使用他们的时候您也不被要求说明类型变量。一个不带类型变量的泛型类型被认为是一个未经处理的类型(raw type)。这样,5.0版本以前的java代码仍然能够运行:您显式的编写所有类型转换就像您已经这样写的一样,您可能会被一些来自编译器的麻烦所困扰。查看下列存储不同类型的对象到一个未经处理的List:
List l = new ArrayList();
l.add("hello");
l.add(new Integer(123));
Object o = l.get(0);
这段代码在java1.4下运行得很好。如果您用java5.0来编译它,javac编译了,但是会打印出这样的“抱怨”:
Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
如果我们加入-Xlint参数后重新编译,我们会看到这些警告:
Test.java:6: warning: [unchecked]
unchecked call to add(E) as a member of the raw type java.util.List
l.add("hello");
Test.java:7: warning: [unchecked]
unchecked call to add(E) as a member of the raw type java.util.List
l.add(new Integer(123));
编译在add()方法的调用上给出了警告,因为它不能够确信加入到list中的值具有正确的类型。它告诉我们说我们使用了一个未经处理的类型,它不能验证我们的代码是类型安全的。注意,get()方法的调用是没有问题的,因为能够被获得的元素已经安全的存在于list中了。
如果您不想使用任何的java5.0的新特性,您可以简单的通过带-source1.4标记来编译他们,这样编译器就不会再“抱怨”了。如果您不能这样做,您可以忽略这些警告,通过使用一个 “@SuppressWarnings("unchecked")”注解(查看本章的4.3节)隐瞒这些警告信息或者升级您的代码,加入类型变量描述。[2]下列示例代码,编译的时候不再会有警告但仍然允许您往list中放入不同的类型的对象。
List l = new ArrayList();
l.add("hello");
l.add(123); // autoboxing
Object o = l.get(0);
参数化类型的体系
参数化类型有类型体系,就像一般的类型一样。这个体系基于对象的类型,而不是变量的类型。这里有些例子您可以尝试:
ArrayList l = new ArrayList();
List m = l; // okay
Collection n = l; // okay
ArrayList o = l; // error
Collection p = (Collection)l; // error, even with cast
一个List是一个Collection,但不是一个List。这句话不容易理解,如果您想理解为什么泛型这样做,这段值得看一下。考察这段代码:
List li = new ArrayList();
li.add(123);
// The line below will not compile. But for the purposes of this
// thought-experiment, assume that it does compile and see how much
// trouble we get ourselves into.
List lo = li;
// Now we can retrieve elements of the list as Object instead of Integer
Object number = lo.get(0);
// But what about this
lo.add("hello world");
// If the line above is allowed then the line below throws ClassCastException
Integer i = li.get(1); // Can't cast a String to Integer!
这就是为什么List不是一个List的原因,虽然List中所有的元素事实上是一个Object的实例。如果允许转换成List,那么转换后,理论上非整型的对象也将被允许添加到list中。
1
运行时类型安全
就像我们所见到的,一个List不允许被转换为一个List,即使这个X能够被转换为Y。然而,一个List能够被转换为一个List,这样您就可以通过继承的方法来做这样的事情。
这种将参数化类型转换为非参数化类型的能力对于向下兼容是必要的,但是它会在泛型所带来的类型安全体系上凿个 漏洞
// Here's a basic parameterized list.
List li = new ArrayList();
// It is legal to assign a parameterized type to a nonparameterized variable
List l = li;
// This line is a bug, but it compiles and runs.
// The Java 5.0 compiler will issue an unchecked warning about it.
// If it appeared as part of a legacy class compiled with Java 1.4, however,
// then we'd never even get the warning.
l.add("hello");
// This line compiles without warning but throws ClassCastException at runtime.
// Note that the failure can occur far away from the actual bug.
Integer i = li.get(0);
泛型仅提供了编译期的类型安全。如果您使用java5.0的编译器来编译您的代码并且没有得到任何警告,这些编译器的检查能够确保您的代码在运行期也是类型安全的。如果您获得了警告或者使用了像未经处理的类型那样修改您的集合的代码,那么您需要增加一些步骤来确保运行期的类型安全。您可以通过使用java.ut