7.4 Avoiding Unnecessary Typecasts
While for/in is nice, you still have to perform all those typecasts, as seen in Example 7-1. If you recall from Chapter 2, though, Tiger introduces
some pretty powerful
language structures in the form of generics. The
main thrust of those new structures is to increase type safety, and that,
paired with what you already know about for/in, can increase the convenience
of this new loop. You can get away with being specific in your
loop iterations, rather than getting an Object from each iteration and
then casting that Object to the appropriate type.
7.4.1 How do I do that?
The first step in working with type-specific iterators actually has to occur
before you ever type for/in. You need to declare your collections using
generics:
Then write your code normally, except substitute a type-specific variable
// These are collections we'll iterate over below.
List<String> wordlist = new ArrayList<String>( );
Set<String> wordset = new HashSet<String>( );
instead of a generic Object and remove any typecasts:
Example 7-1.
for(String word : wordlist) {
System.out.print(word + " ");
}
Example 7-2. Using the for/in loop with generics
The output is identical to that from Example 7-1 (previously shown in "Iterating over Collections"), so I've omitted it here.
package com.oreilly.tiger.ch07;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ForInGenericsDemo {
public static void main(String[] args) {
// These are collections we'll iterate over below.
List<String> wordlist = new ArrayList<String>( );
Set<String> wordset = new HashSet<String>( );
// We start with a basic loop over the elements of an array.
// The body of the loop is executed once for each element of args[].
// Each time through one element is assigned to the variable word.
System.out.println("Assigning arguments to lists...");
for(String word : args) {
System.out.print(word + " ");
wordlist.add(word);
wordset.add(word);
}
System.out.println( );
// Iterate through the elements of the List now.
// Since lists have an order, these words should appear as above
System.out.println("Printing words from wordlist " +
"(ordered, with duplicates)...");
for(String word : wordlist) {
System.out.print(word + " ");
}
System.out.println( );
// Do the same for the Set. The loop looks the same but by virtue of
// using a Set, we lose the word order and also discard duplicates.
System.out.println("Printing words from wordset " +
"(unordered, no duplicates)...");
for(String word : wordset) {
System.out.print(word + " ");
}
}
}
7.4.2 What just happened?
When using generics in this manner, you're essentially offloading all the
typecasting (and possible ClassCastExceptions) onto the compiler, rather
than dealing with them at runtime. When the collections were declared,
the use of generics (<String>, syntactically) allowed the compiler to limit
the accepted types passed into the collections. As a nice side effect, your
for/in statement can make this same assumption, and since the compiler
checks all of this at compile-time, rather than runtime, your code
works, and saves you some typecasting in the process.