Design with runtime class information

How to make use of runtime class information in Java programs

1 2 3 Page 3
Page 3 of 3

// In file rtci/ex4/ObjectSorter.java import java.util.*;

public class ObjectSorter {

private HashMap hm = new HashMap();

public void add(Object o) {

Class key = o.getClass();

if (hm.containsKey(key)) {

ArrayList value = (ArrayList) hm.get(key); value.add(o); } else {

ArrayList value = new ArrayList(); value.add(o); hm.put(key, value); } }

// Returns an iterator for all the Class objects public Iterator getClassIterator() { return (hm.keySet()).iterator(); }

public Collection getObjects(Class key) { return (Collection) hm.get(key); } }

This ObjectSorter class was inspired by a class originally designed by Larry O'Brien and ultimately presented by Bruce Eckel in the "Design Patterns" chapter of his book, Thinking in Java. (My version of the class uses Java 2 collections and follows my own naming sensibilities.) Class ObjectSorter sorts objects passed to its add() method based on class, by placing objects into a HashMap using the objects' Class object as a key.

Here's an application that uses ObjectSorter:

// In file rtci/ex4/Example4.java import java.util.Iterator; import java.util.Collection;

class Example4 {

public static void main(String[] args) {

ObjectSorter os = new ObjectSorter();

os.add(new String("Hi")); os.add(new String("There")); os.add(new String("!")); os.add(new String("What's")); os.add(new String("Happening")); os.add(new String("?")); os.add(new Object()); os.add(new Object[3]); os.add(new Object[3]); os.add(new Object[3]); os.add(new Object[2]); os.add(new Object[2]); os.add(new Example4()); os.add(new Example4()); os.add(new Example4()); os.add(new Example4());

Iterator classIt = os.getClassIterator();

while (classIt.hasNext()) {

Class key = (Class) classIt.next();

Collection col = os.getObjects(key);

String className = key.getName(); int objectCount = col.size(); System.out.println(className + ": " + objectCount); } } }

The Example4 application prints:

[Ljava.lang.Object;: 5
java.lang.String: 6
java.lang.Object: 1
Example4: 4

Example4 demonstrates that the ObjectSorter is able to assign objects into bins by class. Note that all five one-dimensional arrays of Object share the same class, even though some are of different lengths. The length of an array does not affect its class -- just its dimensionality and element type. (The string "[Ljava.lang.Object" is a type descriptor that means one-dimensional array of element type java.lang.Object.) Example4 itself uses runtime class information when it calls getName() on the Class object to print out the name.

Guidelines

To summarize the advice given in this article, here are four guidelines:

Try to keep the types of your variables as high up the inheritance hierarchy as possible.

This guideline encourages you to treat objects as generically as possible, which in turn encourages you to take advantage of dynamic binding and polymorphism. When the type of a reference is a supertype of the actual class of the referenced object, the JVM will use dynamic binding to locate the correct implementation of a method you invoke on that reference.

Prefer polymorphism and dynamic binding over instanceof and downcasting.

This guideline encourages you to program in the object-oriented way, letting polymorphism and dynamic binding work for you. A telltale sign of code that is disobeying this fundamental rule of thumb is a series of if-else statements doing instanceof tests.

Prefer code that 'knows what's coming.'

This guideline counterbalances the above guidelines, stating that although you should make the type of your variables as far up the inheritance hierarchy as possible, you shouldn't make them so far up that you can't use the object without downcasting. You should give variables the type furthest up the inheritance hierarchy that still offers the methods you need to manipulate the object. That very type is the type of object you know is coming.

Use instanceof to ask 'What can you do for me?' and downcasting to access the functionality.

When it comes time to ask the question, What can you do for me? prefer instanceof over reflection. You may know you have a reference to some subclass of Animal and may invoke methods declared in Animal on that object. But you still may want to also invoke playFetch() on the object if it is a Dog. Rather than using reflection to look for a playFetch() method, use instanceof to see if the object really is a Dog. Or perhaps better yet, use instanceof to see if the object implements the PlaysFetch interface.

Once you determine that an object is an instance of some class or interface in which you are interested, use downcasting -- not reflection -- to get at the interesting methods. For example, once you determine you have an Animal object that implements the PlaysFetch interface, don't use reflection to invoke playFetch() on the Animal reference. Instead, downcast the Animal reference to PlaysFetch, and invoke playFetch on the PlaysFetch reference.

In short, don't use reflection for mainstream designs. Use it only for things like bean builders, object serialization mechanisms, object inspectors and debuggers.

Next month

In next month's Design Techniques article, I'll talk about designing with type information.

A request for reader participation

I encourage your comments, criticisms, suggestions, flames -- all kinds of feedback -- about the material presented in this column. If you disagree with something, or have something to add, please let me know.

You can either participate in a discussion forum devoted to this material, enter a comment via the form at the bottom of the article, or e-mail me directly using the link provided in my bio below.

Bill Venners has been writing software professionally for 12 years. Based in Silicon Valley, he provides software consulting and training services under the name Artima Software Company. Over the years he has developed software for the consumer electronics, education, semiconductor, and life insurance industries. He has programmed in many languages on many platforms: assembly language on various microprocessors, C on Unix, C++ on Windows, Java on the Web. He is author of the book: Inside the Java Virtual Machine, published by McGraw-Hill.

Learn more about this topic

Related:
1 2 3 Page 3
Page 3 of 3