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 3 of 5

Here's an example showing how downcasting and instanceof can be used to the detriment of flexibility:

// In file rtci/ex5/Animal.java
class Animal {
    //...
}

// In file rtci/ex5/Dog.java class Dog extends Animal { public void woof() { System.out.println("Woof!"); } //... }
// In file rtci/ex5/Cat.java class Cat extends Animal { public void meow() { System.out.println("Meow!"); } //... }
// In file rtci/ex5/Hippopotamus.java class Hippopotamus extends Animal { public void roar() { System.out.println("Roar!"); } //... }
// In file rtci/ex5/Example5.java class Example5 {
public static void main(String[] args) {
makeItTalk(new Cat()); makeItTalk(new Dog()); makeItTalk(new Hippopotamus()); }
public static void makeItTalk(Animal animal) {
if (animal instanceof Cat) { Cat cat = (Cat) animal; cat.meow(); } else if (animal instanceof Dog) { Dog dog = (Dog) animal; dog.woof(); } else if (animal instanceof Hippopotamus) { Hippopotamus hippopotamus = (Hippopotamus) animal; hippopotamus.roar(); } } }


Although functionally the previous example is correct, I am hoping its design will trigger alarms in the object-oriented brains of most readers. The makeItTalk() method's use of instanceof and downcasting represent one of the fundamental ways runtime class information can be abused in Java programs.

The trouble with this approach is that if you add a new Animal subtype, say Orangutan, you would also have to add a new else-if clause to the makeItTalk() method. Polymorphism and dynamic binding will take care of this for you automatically. (Polymorphism means you can use a variable of a superclass type to hold a reference to an object whose class is the superclass or any of its subclasses. Dynamic binding means the JVM will decide at runtime which method implementation to invoke based on the class of the object. For more on these concepts, see Resources.)

Your object-oriented brain will hopefully be more comfortable with the next version of the program, in which downcasting is cast aside in favor of polymorphism and dynamic binding.

// In file rtci/ex6/Animal.java
abstract class Animal {
    public abstract void talk();
    //...
}

// In file rtci/ex6/Dog.java class Dog extends Animal { public void talk() { System.out.println("Woof!"); } //... }
// In file rtci/ex6/Cat.java class Cat extends Animal { public void talk() { System.out.println("Meow!"); } //... }
// In file rtci/ex6/Hippopotamus.java class Hippopotamus extends Animal { public void talk() { System.out.println("Roar!"); } //... }
// In file rtci/ex6/Example6.java class Example6 {
public static void main(String[] args) {
makeItTalk(new Cat()); makeItTalk(new Dog()); makeItTalk(new Hippopotamus()); }
public static void makeItTalk(Animal animal) {
animal.talk(); } }


Because class Animal declares a talk() method that Dog, Cat, and Hippopotamus override, the makeItTalk() method only needs to invoke talk() on the Animal reference. Dynamic binding ensures that the correct version of talk() will be invoked. If the Animal passed to makeItTalk() is a Dog, makeItTalk() will invoke Dog's implementation of talk(), which says, "Woof!".

Polymorphism and dynamic binding enable you to write code that doesn't need to know about subtypes. You don't have to use runtime class information via the instanceof operator precisely because when you invoke an instance method, the JVM uses runtime class information to figure out which implementation of the method to execute.

Legitimate uses of downcast and instanceof

Although downcasting and instanceof can be abused as described above, they also have legitimate uses. One common use of a downcast is to cast an Object reference extracted from a Collection to a more specific subtype. Here's an example:

class Hippopotamus {

private String yawnAdjective;
Hippopotamus(String yawnAdjective) { this.yawnAdjective = yawnAdjective; }
void yawn() { System.out.println(yawnAdjective + " yawn!"); } }
// In file rtci/ex1/Example1.java import java.util.ArrayList; import java.util.Collection; import java.util.Iterator;
class Example1 {
public static void main(String[] args) {
ArrayList hippos = new ArrayList(); hippos.add(new Hippopotamus("Big")); hippos.add(new Hippopotamus("Little")); hippos.add(new Hippopotamus("Technicolor"));
makeHipposYawn(hippos); }
// Client must pass a collection of Hippopotami static void makeHipposYawn(Collection hippos) {
Iterator it = hippos.iterator(); while (it.hasNext()) { Hippopotamus hippo = (Hippopotamus) it.next(); hippo.yawn(); } } }


In this example, Iterator's next() method returns a reference to a Hippopotamus object, but the type of the reference is Object. The reference is immediately downcast to Hippopotamus, so that the yawn() method can be invoked on the object.

Note that instanceof was not needed here, because a precondition of the makeHipposYawn() method states that the Collection, passed in as parameter hippos, be full of Hippopotamus objects. Nevertheless, this code makes use of runtime class information because the JVM checks to see whether the cast is valid. In other words, the JVM makes sure the object referenced by the return value of next() really is a Hippopotamus. If not, the JVM will throw a ClassCastException and the makeHipposYawn() method will complete abruptly.

instanceof and Can you dance()?

The primary use of instanceof is to find out whether you can perform some kind of operation on an object. In the next example, class DancingHippopotamus (a subclass of Hippopotamus) declares a dance() method. The makeHipposDance() method of class Example2 assumes it is receiving a collection of hippopotami, but not necessarily DancingHippopotamus objects. It uses instanceof to find out whether each object in the collection is an instance of DancingHippopotamus objects. For each instance of DancingHippopotamus it finds, it downcasts the reference to DancingHippopotamus and invokes dance().

// In file rtci/ex2/Hippopotamus.java
class Hippopotamus {

private String yawnAdjective;
Hippopotamus(String yawnAdjective) { this.yawnAdjective = yawnAdjective; }
void yawn() { System.out.println(yawnAdjective + " yawn!"); } }
// In file rtci/ex2/DancingHippopotamus.java class DancingHippopotamus extends Hippopotamus {
DancingHippopotamus(String yawnAdjective) { super(yawnAdjective); }
void dance() { System.out.println("Dance!"); } }
// In file rtci/ex2/Example2.java import java.util.ArrayList; import java.util.Collection; import java.util.Iterator;
class Example2 {
public static void main(String[] args) {
ArrayList hippos = new ArrayList(); hippos.add(new Hippopotamus("Big")); hippos.add(new DancingHippopotamus("Little")); hippos.add(new Hippopotamus("Technicolor"));
makeHipposYawn(hippos); makeHipposDance(hippos); }
// Client must pass a collection of Hippopotami static void makeHipposYawn(Collection hippos) {
Iterator it = hippos.iterator(); while (it.hasNext()) { Hippopotamus hippo = (Hippopotamus) it.next(); hippo.yawn(); } }
// Client must pass a collection of Hippopotami static void makeHipposDance(Collection hippos) {
Iterator it = hippos.iterator(); while (it.hasNext()) { Object hippo = it.next(); if (hippo instanceof DancingHippopotamus) { DancingHippopotamus dh = (DancingHippopotamus) hippo; dh.dance(); } } } }


The previous example highlights the primary legitimate use of instanceof: to find out whether an object can do something for you (in this case, dance). When instanceof returns true, the reference is downcast to a more specific type so methods can be invoked.

  • 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