Java 1.5 Tiger A Developers Notebook [Electronic resources]

David Flanagan, Brett McLaughlin

نسخه متنی -صفحه : 131/ 27
نمايش فراداده

2.3 Iterating Over Parameterized Types

Although the for/in loop provides a means of almost completely avoiding the java.util.Iterator class, that particular feature of Tiger isn't covered until Chapter 7. But until you get to that chapter (and probably occasionally after that), it's still useful to know how generic collection types affect Iterator. You'll need to perform an extra step to get the full power of generics.

2.3.1 How do I do that?

It would seem that once you've parameterized your collections, grabbing an Iterator and using it would be trivial:

    List<String> listOfStrings = new LinkedList<String>( );
listOfStrings.add("Happy");
listOfStrings.add("Birthday");
listOfStrings.add("To");
listOfStrings.add("You");
for (Iterator i = listOfStrings.iterator( ); i.hasNext( ); ) {
String s = i.next( );
out.println(s);
}

However, all is not well. Here's what the compiler spits back to you:

       [javac] code\src\com\oreilly\tiger\ch02\GenericsTester.java:54:
incompatible types
[javac] found   : java.lang.Object
[javac] required: java.lang.String
[javac]       String s = i.next( );
[javac]                        ^
[javac] Note: code\src\com\oreilly\tiger\ch02\GenericsTester.java
uses unchecked or unsafe operations.
[javac] Note: Recompile with -Xlint:unchecked for details.
[javac] 1 error

The problem here is that while you've parameterized your List, you haven't parameterized your Iterator. It's still spitting out Objects, and doesn't know that it should only expect to receive and respond with String types. Just like the collections, Iterator is a generic type in Java, and is declared as public interface Iterator<E>. Its next( ) method, then, returns E (which is a placeholder, as detailed in "Using Type-Safe Lists"). To parameterize it, you use the same syntax as you did for collection classes:

    List<String> listOfStrings = new LinkedList<String>( );
listOfStrings.add("Happy");
listOfStrings.add("Birthday");
listOfStrings.add("To");
listOfStrings.add("You");
for (Iterator<String> i = listOfStrings.iterator( ); i.hasNext( ); ) {
String s = i.next( );
out.println(s);
}

Now this Iterator only works with String types, and your code compiles (and runs) normally. You should always pair your Iterators with your collections like thisif the collection is parameterized, the Iterator should use the same parameter.

2.3.2 What about...

...if you define a typesafe Iterator, for a collection that isn't typesafe. Well, you're basically playing with fire, and assuming that someone filled the collection correctly. The following code, for example, compiles and runs without a problem (although you'll get lint warnings, detailed in "Checking for Lint":

     public void testTypeSafeIterators(PrintStream out) throws IOException {
List listOfStrings = new LinkedList( );
listOfStrings.add("Happy");
listOfStrings.add("Birthday");
listOfStrings.add("To");
listOfStrings.add("You");
for (Iterator<String> i = listOfStrings.iterator( ); i.hasNext( ); ) {
String s = i.next( );
out.println(s);
}
}

In this case, it was probably just an oversight that List wasn't also parameterized. However, the following code also compiles fine, but fails horribly at runtime:

     public void testTypeSafeIterators(PrintStream out) throws IOException {
List listOfStrings = getList( );
for (Iterator<String> i = listOfStrings.iterator( ); i.hasNext( ); ) {
String s = i.next( );
out.println(s);
}
}
private List getList( ) {
List list = new LinkedList( );
list.add(3);
list.add("Blind");
list.add("Mice");
return list;
}

The getList( ) method, which presumably could have been coded by a trusted (or even a non-trusted) source, is supposed to return only String objects (at least, that's inferred by the use of Iterator<String>). However, it adds a numeric object (an int that gets boxed into an Integer), and at runtime a nasty ClassCastException pops up. This is why you should always parameterize your collections if you want to parameterize your Iterators. If you took that step, you'd get an error at compile-time when trying to assign the List returned from getList( ) (which is not parameterized) to a List<String>. That error protects you from problems just like this one.