The Artima Developer Community
Sponsored Link

Designing with Runtime Class Information
Using Runtime Class Information in Java Programs
by Bill Venners
First Published in JavaWorld, January 1999

<<  Page 2 of 6  >>

Advertisement

How to get some action
Now that you know that all this class information is available for every object on the heap, how do you get at it? Java gives you many ways to make use of the information stored in the method area:

Upcasts
Perhaps the simplest way to use runtime class information is to perform a cast. As one of Java's security mechanisms, JVMs check all casts at runtime for validity. If you have a reference of type Object and try to cast it to type Hippopotamus, for example, the JVM will in some way check to make sure the cast is valid. This check makes use of runtime class information. If the cast isn't valid, the JVM will throw a ClassCastException exception.

Casts, depending on how they are used, can help or hinder code flexibility. It is usually helpful to upcast -- to cast from a subtype to a supertype. A basic guideline for making flexible object-oriented designs is to treat objects as generically as possible -- to make the type of your variables as far up the inheritance hierarchy as possible.

If you have code that must manipulate a Hippopotamus object, for example, you can hold a reference to it in a variable of type Hippopotamus. But if your class Hippopotamus extends class Animal and what you are doing is something you might consider doing to any kind of animal, not just hippopotami, you should consider upcasting your reference to Animal. You could use that same code later on for instances of other subclasses of Animal, such as Cats or Dogs, not just Hippopotamus objects.

Downcasts and instanceof
In contrast to upcasts, downcasts (casts from a supertype to a subtype) can be more troublesome to flexibility. In general, instead of doing explicit downcasts, you should strive to let dynamic binding make polymorphism work for you.

Downcasts often are used in combination with the instanceof operator. instanceof is another of Java's mechanisms that use runtime class information. When the JVM executes an instanceof operation, it consults the runtime class information associated with an object reference to determine whether the object is or is not an instance of the specified class. Like downcasts, instanceof can potentially work against the goal of flexibility.

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.

<<  Page 2 of 6  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us