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) { // This list can only hold String objects List wordlist = new ArrayList (); // args is a String[], not String, so the compiler won't let us do this wordlist.add(args); // Compilation error! // We can do this, though. // Notice the use of the new for/in looping statement for(String arg : args) wordlist.add(arg); // No cast is required. List .get() returns a String. String word = 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];
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中放入不同的类型的对象。
ArrayList l = new ArrayList ();List m = l; // okayCollection n = l; // okayArrayList o = l; // errorCollection p = (Collection)l; // error, even with cast
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 IntegerObject number = lo.get(0);// But what about this?lo.add("hello world");// If the line above is allowed then the line below throws ClassCastExceptionInteger 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 variableList 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 safetyList cli = Collections.checkedList(li, Integer.class);// Now widen the checked list to the raw typeList l = cli; // This line compiles but fails at runtime with a ClassCastException.// The exception occurs exactly where the bug is, rather than far awayl.add("hello");
List [] wordlists = new ArrayList [10];ArrayList ali = new ArrayList ();ali.add(123);Object[] objs = wordlists;objs[0] = ali; // No ArrayStoreExceptionString s = wordlists[0].get(0); // ClassCastException!
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<?>(); 创建一个不知道类型的List是毫无道理的。如果您创建了它,那么您必须知道它将保持的元素是什么类型的。您可以在随后的方法中不关心元素类型而去遍历这里list,但是您需要在您创建它的时候描述元素的类型。如果你确实需要一个List来保持任何类型,那么您只能这么写:
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;}