Using Foreach Loops in J2SE 1.5, Page 2
Supporting Foreach in Your Own Class
Suppose you are building a Catalog class. A Catalog object collects any number of Product objects. You store these objects using an ArrayList instance variable defined in Catalog. Code working with a Catalog object will frequently need to iterate through the entire list of products, to do something with each object in turn.
Here is how the Catalog class might look:
import java.util.*;
class Catalog {
private List<Product> products = new ArrayList<Product>();
void add(Product product) {
products.add(product);
}
}
The Product class includes a method that allows you to discount the price on a product:
class Product {
private String id;
private String name;
private BigDecimal cost;
Product(String id, String name, BigDecimal cost) {
this.id = id;
this.name = name;
this.cost = cost;
}
void discount(BigDecimal percent) {
cost = cost.multiply(new BigDecimal("1.0").subtract(percent));
}
public String toString() {
return id + ": " + name + " @ " + cost;
}
}
A (Poor) Solution
To allow client code to work with all products, you could create a method in Catalog that returned the ArrayList of products:
// don't do this
List<Product> getProducts() {
return products;
}
Client code could iterate through the list however they chose. However, returning a collection to a client is a bad idea. By giving the client your collection, you have relinquished any control over the contents of that collection. Client code could add or remove elements to the collection without the knowledge of the Catalog object. You've also burdened the client with more work than necessary.
The Iterable Interface
Instead, you can designate the Catalog class as being "iterable." Making a class iterable tells clients that they can iterate through its contents using a foreach loop. Sun has added to Java a new interface, java.lang.Iterable, that allows you to mark your classes as iterable:
package java.lang;
import java.util.Iterator;
public interface Iterable<T> {
Iterator<T> iterator();
}
To implement this interface in Catalog, you must code an
iterator method. This method must return an Iterator object that is
bound to the Product type.
The Catalog class stores all of its Product objects in an ArrayList. The
easiest way to provide an Iterator object to a client is to simply return the
Iterator object from the products ArrayList itself. Here is the
modified Catalog class:
class Catalog implements Iterable<Product> {
private List<Product> products = new ArrayList<Product>();
void add(Product product) {
products.add(product);
}
public Iterator<Product> iterator() {
return products.iterator();
}
}
Note that the Iterator, the List reference, and the ArrayList are all
bound to the Product type.
Once you have implemented the java.lang.Iterable interface, client code can use the foreach loop. Here is a bit of example code:
Catalog catalog = new Catalog();
catalog.add(new Product("1", "pinto", new BigDecimal("4.99")));
catalog.add(new Product("2", "flounder", new BigDecimal("64.88")));
catalog.add(new Product("2", "cucumber", new BigDecimal("2.01")));
for (Product product: catalog)
product.discount(new BigDecimal("0.1"));
for (Product product: catalog)
System.out.println(product);
Summary
The foreach loop provides a simple, consistent solution for iterating arrays, collection classes, and even your own collections. It eliminates much of the repetitive code that you would otherwise require. The foreach loop eliminates the need for casting as well as some potential problems. The foreach loop is a nice new addition to Java that was long overdue. I greatly appreciate the added simplicity in my source code.
Trying it Out
You can download the current beta version of J2SE 1.5 from Sun's web site.
Jeff Langr is a freelance author and the owner of Langr Software Solutions. He is working on a second book on Java and test-driven development entitled Agile Java, due out from Prentice Hall in fall 2004.
