Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Design with runtime class information

How to make use of runtime class information in Java programs

  • Print
  • Feedback

Page 5 of 5

So how can you take advantage of java.lang.Class and reflection in your designs? In general, you should avoid using reflection in your designs. Reflection is intended for special cases like JavaBeans builder tools, object serialization mechanisms, object inspectors, debuggers, and so on. Reflection is useful in situations in which you have absolutely no idea what's coming. When a programmer drops a JavaBean into a bean builder, for example, the builder hasn't a clue about the class of the bean. The builder must use reflection to introspect the bean class to discover its class, its properties, its methods, and so on. Because the bean builder has no idea what's coming, it must use reflection to figure out how to use the beans it is given.

One situation in which you may know what's coming, but will still want to use java.lang.Class and possibly the reflection API, is when you are creating an instance of a class loaded into a different name space. One of the main reasons for using class loaders and dynamic extension is so your program doesn't have to know at compile time all the classes and interfaces it will load and use at runtime. Although you should prefer designs in which you assume that dynamically loaded objects will implement a known interface or descend from a known class, in order to instantiate an object of the dynamically loaded class, you'll still have to use either Class.newInstance() or the newInstance() method of class java.lang.reflect.Constructor().

Whenever possible, you should design programs such that they know what's coming to a sufficient extent that objects can be used without consulting instanceof or reflection. In cases where you need to ask an object, Can you do a particular thing for me?, you should choose instanceof over reflection to make the query.

Creative uses of java.lang.Class

To every rule, of course, there are exceptions, and this is also true of the rule that you should always strive to design code that knows what's coming. Occasionally, I have encountered a few creative uses of runtime class information that I felt didn't negatively impact flexibility and that made for a good design. In these situations, the code doesn't know what's coming, but, nevertheless, probably represents the best way to offer the services provided.

The most common examples of reasonably-designed Java classes that don't know what's coming are collection classes. Collection classes accept references of type Object to add to their collection, so they can be used with any type of object. Since Java objects all share a common base class (java.lang.Object), and since Java doesn't have templates like C++, accepting Object references in the add() method probably is the best way to design collection classes in Java.

For another example of a useful class that doesn't know what's coming, take a look at this ObjectSorterclass:

// 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.

About the author

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.

Read more about Core Java in JavaWorld's Core Java section.

  • Print
  • Feedback

Resources
  • Bill's next book is Flexible Java http://www.artima.com/flexiblejava/index.html
  • A complete online reprint of "Chapter 7, The Linking Model," of Bill's book Inside the Java Virtual Machine http://www.artima.com/insidejvm/linkmod.html
  • The handout and slides for Bill's "Dynamic Extension in Java" talk. http://www.artima.com/javaseminars/modules/DynaExt/index.html
  • Bill recently returned from his European bike trip. Read about it at
    http://www.artima.com/bv/travel/bike98/index.html
  • The discussion forum devoted to the material presented in this article http://www.artima.com/flexiblejava/fjf/rtci/index.html
  • Links to all previous Design Techniques articles http://www.artima.com/designtechniques/index.html
  • Recommended books on Java design http://www.artima.com/designtechniques/booklist.html
  • A transcript of an e-mail debate between Bill Venners, Mark Johnson (JavaWorld's JavaBeans columnist), and Mark Balbe on whether all objects should be made into beans http://www.artima.com/flexiblejava/comments/beandebate.html
  • Object orientation FAQ http://www.cyberdyne-object-sys.com/oofaq/
  • A hearty collection of links on object orientation (numbering 7,237 at last count) http://www.rhein-neckar.de/~cetus/software.html
  • The Object-Oriented Page http://www.well.com/user/ritchie/oo.html
  • Collection of information on OO approach http://arkhp1.kek.jp:80/managers/computing/activities/OO_CollectInfor/OO_CollectInfo.html
  • Design Patterns home page http://hillside.net/patterns/patterns.html
  • A Comparison of OOA and OOD Methods http://www.iconcomp.com/papers/comp/comp_1.html
  • "Object-Oriented Analysis and Design MethodsA Comparative Review" http://wwwis.cs.utwente.nl:8080/dmrg/OODOC/oodoc/oo.html
  • Patterns discussion FAQ http://gee.cs.oswego.edu/dl/pd-FAQ/pd-FAQ.html
  • Patterns in Java AWT http://mordor.cs.hut.fi/tik-76.278/group6/awtpat.html
  • Software Technology's Design Patterns page http://www.sw-technologies.com/dpattern/
  • Previous Design Techniques articles http://www.javaworld.com/topicalindex/jw-ti-techniques.html