public static void main(String[] args) { // This list is intended to hold only strings. // The compiler doesn't know that so we have to remember ourselves. List wordlist = new ArrayList();
// Oops! We added a String[] instead of a String. // The compiler doesn't know that this is an error. wordlist.add(args);
// Since List can hold arbitrary objects, the get() method returns // Object. Since the list is intended to hold strings, we cast the // return value to String but get a ClassCastException because of // the error above. String word = (String)wordlist.get(0); }
public static void main(String[] args) { // A map from strings to their position in the args[] array Map map = new HashMap();
// Note that we use autoboxing to wrap i in an Integer object. for(int i=0; i < args.length; i++) map.put(args[i], i);
// Find the array index of a word. Note no cast is required! Integer position = map.get("hello");
// We can also rely on autounboxing to convert directly to an int, // but this throws a NullPointerException if the key does not exist // in the map int pos = map.get("world"); }
// Look at all those nested angle brackets! Map>> map = getWeirdMap();
// The compiler knows all the types and we can write expressions // like this without casting. We might still get NullPointerException // or ArrayIndexOutOfBounds at runtime, of course. int value = map.get(key).get(0).get(0)[0];
// Here's how we break that expression down step by step. List> listOfLists = map.get(key); List listOfIntArrays = listOfLists.get(0); int[] array = listOfIntArrays.get(0); int element = array[0];
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中了。
ArrayList l = new ArrayList(); List m = l; // okay Collection n = l; // okay ArrayList o = l; // error Collection p = (Collection)l; // error, even with cast
// 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!
// 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);
// Here's a basic parameterized list. List li = new ArrayList();
// Wrap it for runtime type safety List cli = Collections.checkedList(li, Integer.class);
// Now widen the checked list to the raw type List l = cli;
// This line compiles but fails at runtime with a ClassCastException. // The exception occurs exactly where the bug is, rather than far away l.add("hello");
public static void printList(PrintWriter out, List list) { for(int i=0, n=list.size(); i < n; i++) { if (i > 0) out.print(", "); out.print(list.get(i).toString()); } }
public static void printList(PrintWriter out, List list) { for(int i=0, n=list.size(); i < n; i++) { if (i > 0) out.print(", "); out.print(list.get(i).toString()); } }
public static void printList(PrintWriter out, List<?> list) { for(int i=0, n=list.size(); i < n; i++) { if (i > 0) out.print(", "); Object o = list.get(i); out.print(o.toString()); } }
这个版本的方法能够被编译过,没有警告,而且能够在任何我们希望使用的地方使用。通配符“?表示一个未知类型,类型List<?>被读作“List of unknown 作为一般原则,如果类型是泛型的,同时您并不知道或者并不关心值的类型,您应该使用“?通配符来代替一个未经处理的类型。未经处理的类型被允许仅是为了向下兼容,而且应该只能够被允许出现在老的代码中。注意,无论如何,您不能在调用构造器时使用通配符。下面的代码是非法的: List<?> l = new ArrayList<?>();
public static double sumList(List<?> list) { double total = 0.0; for(Object o : list) { Number n = (Number) o; // A cast is required and may fail total += n.doubleValue(); } return total; }
脚注 [1] 在本章中,我会坚持用术语泛型类型来指一个声明一个或多个类型变量的类型,用参数化的类型来指由实际类型参数来替换其类型变量的泛型类型。然而,在一般情况下,这种区别并不明显,并且这些术语有时通用。 [2] 在撰写本文时候,javac并不支持@SuppressWarnings 的注解。期望在Java 5.1中得到支持。 [3] 本节所示的3个printList()方法忽略了这样一个事实,即java.util 中List的所有实现类都有一个可用的toString()方法。还要注意这些方法假定List实现RandomAccess并在LinkedList实例中只提供了很差的运行效率。 David Flanagan是众多O'Reilly书籍的作者。这些书包括《Java in a Nutshell》,《Java Examples in a Nutshell》,《Java Foundation Classes in a Nutshell》,《JavaScript: The Definitive Guide》,《JavaScript Pocket Reference》。