The need for RTTI | CodeGuru

The need for RTTI

Bruce Eckel’s Thinking in Java Contents | Prev | Next Consider the now familiar example of a class hierarchy that uses polymorphism. The generic type is the base class Shape, and the specific derived types are Circle, Square, and Triangle: This is a typical class hierarchy diagram, with the base class at the top and […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
9 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

Consider


the now familiar example of a class hierarchy that uses polymorphism. The


generic type is the base class


Shape

,


and the specific derived types are


Circle

,


Square

,


and


Triangle

:

This


is a typical class hierarchy diagram, with the base class at the top and the


derived classes growing downward. The normal goal in object-oriented programming


is for the bulk of your code to manipulate handles to the base type (
Shape,
in this case), so if you decide to extend the program by adding a new class (
Rhomboid,
derived from
Shape,
for example), the bulk of the code is not affected. In this example, the
dynamically bound method in the
Shape
interface is
draw( ),
so the intent is for the client programmer to call
draw( )
through a generic
Shape
handle.
draw( )
is overridden in all of the derived classes, and because it is a dynamically
bound method, the proper behavior will occur even though it is called through a
generic
Shape
handle. That’s
polymorphism.

Thus,


you generally create a specific object (


Circle

,


Square

,


or


Triangle

),


upcast it to a


Shape

(forgetting the specific type of the object), and use that anonymous


Shape

handle


in the rest of the program.

As


a brief review of polymorphism and

upcasting,
you might code the above example as follows: (See page
97
if you have trouble executing this program.)
//: Shapes.java
package c11;
import java.util.*;
 
interface Shape {
  void draw();
}
 
class Circle implements Shape {
  public void draw() {
    System.out.println("Circle.draw()");
  }
}
 
class Square implements Shape {
  public void draw() {
    System.out.println("Square.draw()");
  }
}
 
class Triangle implements Shape {
  public void draw() {
    System.out.println("Triangle.draw()");
  }
}
 
public class Shapes {
  public static void main(String[] args) {
    Vector s = new Vector();
    s.addElement(new Circle());
    s.addElement(new Square());
    s.addElement(new Triangle());
    Enumeration e = s.elements();
    while(e.hasMoreElements())
      ((Shape)e.nextElement()).draw();
  }
} ///:~ 

The


base class could be coded as an


interface

,


an


abstract

class, or an ordinary class. Since


Shape

has


no concrete members (that is, members with definitions), and it’s not


intended that you ever create a plain


Shape

object, the most appropriate and flexible representation is an


interface

.


It’s also cleaner because you don’t have all those


abstract

keywords lying about.

Each


of the derived classes overrides the base-class


draw

method so it behaves differently. In


main( )

,


specific types of


Shape

are created and then added to a


Vector

.


This is the point at which the upcast occurs because the


Vector

holds only


Object

s.


Since everything in Java (with the exception of primitives) is an


Object

,


a


Vector

can also hold


Shape

objects. But during an upcast to


Object

,


it


also loses any specific information, including the fact that the objects are


shape

s.


To the


Vector

,


they are just


Object

s.

At


the point you fetch an element out of the


Vector

with


nextElement( )

,


things get a little busy. Since


Vector

holds only


Object

s,


nextElement( )

naturally produces an


Object

handle. But we know it’s really a


Shape

handle, and we want to send


Shape

messages to that object. So a

cast
to
Shape
is
necessary using the traditional “
(Shape)
cast. This is the most basic form of RTTI, since in Java all casts are checked
at run-time for correctness. That’s exactly what RTTI means: at run-time,
the type of an object is identified.

In


this case, the RTTI cast is only partial: the


Object

is cast to a


Shape

,


and not all the way to a


Circle

,


Square

,


or


Triangle

.


That’s because the only thing we


know

at this point is that the


Vector

is full of


Shape

s.


At compile-time, this is enforced only by your own self-imposed rules, but at


run-time the cast ensures it.

Now


polymorphism takes over and the exact method that’s called for the


Shape

is determined by whether the handle is for a


Circle

,


Square

,


or


Triangle

.


And in general, this is how it should be; you want the bulk of your code to


know as little as possible about


specific

types of objects, and to just deal with the general representation of a family


of objects (in this case,


Shape

).


As a result, your code will be easier to write, read, and maintain, and your


designs will be easier to implement, understand, and change. So polymorphism is


the general goal in object-oriented programming.

But


what if you have a special programming problem that’s easiest to solve if


you know the exact type of a generic handle

?
For example, suppose you want to allow your users to highlight all the shapes
of any particular type by turning them purple. This way, they can find all the
triangles on the screen by highlighting them. This is what RTTI accomplishes:
you can ask a handle to a
Shape
exactly what type it’s referring to.

The
Class object

To


understand how RTTI works in Java, you must first know how type information is


represented at run time. This is accomplished through a special kind of object


called the

Class
object,

which contains information about the class. (This is sometimes called a
meta-class.)
In fact, the
Class
object is used to create all of the “regular” objects of your class.

There’s


a


Class

object for each class that is part of your program. That is, each time you


write a new class, a single


Class

object is also created (and stored, appropriately enough, in an identically


named


.class

file).


At run time, when you want to make an object of that class, the

Java
Virtual Machine (JVM) that’s executing your program first checks to see
if the
Class
object for that type is loaded. If not, the JVM loads it by finding the
.class
file
with that name. Thus, a Java program isn’t completely loaded before it
begins, which is different from many traditional languages.

Once


the


Class

object for that type is in memory, it is used to create all objects of that type.

If


this seems shadowy or if you don’t really believe it, here’s a


demonstration program to prove it:

//: SweetShop.java
// Examination of the way the class loader works
 
class Candy {
  static {
    System.out.println("Loading Candy");
  }
}
 
class Gum {
  static {
    System.out.println("Loading Gum");
  }
}
 
class Cookie {
  static {
    System.out.println("Loading Cookie");
  }
}
 
public class SweetShop {
  public static void main(String[] args) {
    System.out.println("inside main");
    new Candy();
    System.out.println("After creating Candy");
    try {
      Class.forName("Gum");
    } catch(ClassNotFoundException e) {
      e.printStackTrace();
    }
    System.out.println(
      "After Class.forName("Gum")");
    new Cookie();
    System.out.println("After creating Cookie");
  }
} ///:~ 

Each


of the classes


Candy

,


Gum

,


and


Cookie

has a

static
clause that is executed as the class is loaded for the first time. Information
will be printed out to tell you when loading occurs for that class. In
main( ),
the object creations are spread out between print statements to help detect the
time of loading.

A


particularly interesting line is:

Class.forName("Gum");

This


method is a


static

member of


Class

(to which all


Class

objects belong). A


Class

object is like any other object and so you can get and manipulate a handle to


it. (That’s what the loader does.) One of the ways to get a handle to the


Class

object is

forName( ),
which takes a
String
containing the textual name (watch the spelling and capitalization!) of the
particular class you want a handle for. It returns a
Class
handle.

The


output of this program for one JVM is:

inside main
Loading Candy
After creating Candy
Loading Gum
After Class.forName("Gum")
Loading Cookie
After creating Cookie

You


can see that each


Class

object is loaded only when it’s needed, and the


static

initialization is performed upon class loading.

Interestingly


enough, a different JVM yielded:

Loading Candy
Loading Cookie
inside main
After creating Candy
Loading Gum
After Class.forName("Gum")
After creating Cookie

It


appears that this JVM anticipated the need for


Candy

and


Cookie

by examining the code in


main( )

,


but could not see


Gum

because it was created by a call to


forName( )

and not through a more typical call to


new

.


While this JVM produces the desired effect because it does get the classes


loaded before they’re needed, it’s uncertain whether the behavior


shown is precisely correct.


Class
literals

In


Java 1.1


you have a second way to produce the handle to the
Class
object: use the
class
literal
.
In the above program this would look like:
Gum.class;

which


is not only simpler, but also safer since it’s checked at compile time.


Because it eliminates the method call, it’s also more efficient.

Class


literals work with regular classes as well as interfaces, arrays, and primitive


types. In addition, there’s a standard field called

TYPE
that exists for each of the primitive wrapper classes. The
TYPE
field produces a handle to the
Class
object for the associated primitive type, such that:

is equivalent to …

boolean.class Boolean.TYPE
char.class Character.TYPE
byte.class Byte.TYPE
short.class Short.TYPE
int.class Integer.TYPE
long.class Long.TYPE
float.class Float.TYPE
double.class Double.TYPE
void.class Void.TYPE

Checking
before a cast

So


far, you’ve seen RTTI forms including:

  1. The
    classic cast, e.g. “
    (Shape),
    which uses RTTI to make sure the cast is correct and throws a
    ClassCastException
    if you’ve performed a bad cast.
  2. The
    Class
    object representing the type of your object. The
    Class
    object can be queried for useful runtime information.

In


C++, the classic cast “


(Shape)


does


not

perform


RTTI. It simply tells the compiler to treat the object as the new type. In


Java, which does perform the type check, this cast is often called a


“type safe downcast

.”
The reason for the term “downcast” is the historical arrangement of
the class hierarchy diagram. If casting a
Circle
to a
Shape
is an upcast, then casting a
Shape
to a
Circle
is a downcast. However, you know a
Circle
is also a
Shape,
and the compiler freely allows an upcast assignment, but you
don’t
know that a
Shape
is necessarily a
Circle,
so the compiler doesn’t allow you to perform a
downcast
assignment without using an explicit cast.

There’s


a third form of RTTI in Java. This is the

keyword
instanceof
that tells you if an object is an instance of a particular type. It returns a
boolean
so
you use it in the form of a question, like this:
if(x instanceof Dog)
  ((Dog)x).bark();

The


above


if

statement checks to see if the object


x

belongs to the class


Dog
before

casting


x

to a


Dog

.


It’s important to use


instanceof

before a downcast when you don’t have other information that tells you


the type of the object; otherwise you’ll end up with a

ClassCastException.

Ordinarily,


you might be hunting for one type (triangles to turn purple, for example), but


the following program shows how to tally


all

of the objects using


instanceof

.

//: PetCount.java
// Using instanceof
package c11.petcount;
import java.util.*;
 
class Pet {}
class Dog extends Pet {}
class Pug extends Dog {}
class Cat extends Pet {}
class Rodent extends Pet {}
class Gerbil extends Rodent {}
class Hamster extends Rodent {}
 
class Counter { int i; }
 
public class PetCount {
  static String[] typenames = {
    "Pet", "Dog", "Pug", "Cat",
    "Rodent", "Gerbil", "Hamster",
  };
  public static void main(String[] args) {
    Vector pets = new Vector();
    try {
      Class[] petTypes = {
        Class.forName("c11.petcount.Dog"),
        Class.forName("c11.petcount.Pug"),
        Class.forName("c11.petcount.Cat"),
        Class.forName("c11.petcount.Rodent"),
        Class.forName("c11.petcount.Gerbil"),
        Class.forName("c11.petcount.Hamster"),
      };
      for(int i = 0; i < 15; i++)
        pets.addElement(
          petTypes[
            (int)(Math.random()*petTypes.length)]
            .newInstance());
    } catch(InstantiationException e) {}
      catch(IllegalAccessException e) {}
      catch(ClassNotFoundException e) {}
    Hashtable h = new Hashtable();
    for(int i = 0; i < typenames.length; i++)
      h.put(typenames[i], new Counter());
    for(int i = 0; i < pets.size(); i++) {
      Object o = pets.elementAt(i);
      if(o instanceof Pet)
        ((Counter)h.get("Pet")).i++;
      if(o instanceof Dog)
        ((Counter)h.get("Dog")).i++;
      if(o instanceof Pug)
        ((Counter)h.get("Pug")).i++;
      if(o instanceof Cat)
        ((Counter)h.get("Cat")).i++;
      if(o instanceof Rodent)
        ((Counter)h.get("Rodent")).i++;
      if(o instanceof Gerbil)
        ((Counter)h.get("Gerbil")).i++;
      if(o instanceof Hamster)
        ((Counter)h.get("Hamster")).i++;
    }
    for(int i = 0; i < pets.size(); i++)
      System.out.println(
        pets.elementAt(i).getClass().toString());
    for(int i = 0; i < typenames.length; i++)
      System.out.println(
        typenames[i] + " quantity: " +
        ((Counter)h.get(typenames[i])).i);
  }
} ///:~ 

There’s


a rather narrow restriction on


instanceof

in


Java 1.0

:
You can compare it to a named type only, and not to a
Class
object. In the example above you might feel that it’s tedious to write
out all of those
instanceof
expressions, and you’re right. But in Java 1.0 there is no way to
cleverly automate it by creating a
Vector
of
Class
objects and comparing it to those instead. This isn’t as great a
restriction as you might think, because you’ll eventually understand that
your design is probably flawed if you end up writing a lot of
instanceof
expressions.

Of


course this example is contrived – you’d probably put a


static

data member in each type and increment it in the constructor to keep track of


the counts. You would do something like that


if

you had control of the source code for the class and could change it. Since


this is not always the case, RTTI can come in handy.


Using
class literals

It’s


interesting to see how the


PetCount.java

example can be rewritten using Java 1.1


class literals
.
The result is cleaner in many ways:
//: PetCount2.java
// Using Java 1.1 class literals
package c11.petcount2;
import java.util.*;
 
class Pet {}
class Dog extends Pet {}
class Pug extends Dog {}
class Cat extends Pet {}
class Rodent extends Pet {}
class Gerbil extends Rodent {}
class Hamster extends Rodent {}
 
class Counter { int i; }
 
public class PetCount2 {
  public static void main(String[] args) {
    Vector pets = new Vector();
    Class[] petTypes = {
      // Class literals work in Java 1.1+ only:
      Pet.class,
      Dog.class,
      Pug.class,
      Cat.class,
      Rodent.class,
      Gerbil.class,
      Hamster.class,
    };
    try {
      for(int i = 0; i < 15; i++) {
        // Offset by one to eliminate Pet.class:
        int rnd = 1 + (int)(
          Math.random() * (petTypes.length - 1));
        pets.addElement(
          petTypes[rnd].newInstance());
      }
    } catch(InstantiationException e) {}
      catch(IllegalAccessException e) {}
    Hashtable h = new Hashtable();
    for(int i = 0; i < petTypes.length; i++)
      h.put(petTypes[i].toString(),
        new Counter());
    for(int i = 0; i < pets.size(); i++) {
      Object o = pets.elementAt(i);
      if(o instanceof Pet)
        ((Counter)h.get(
          "class c11.petcount2.Pet")).i++;
      if(o instanceof Dog)
        ((Counter)h.get(
          "class c11.petcount2.Dog")).i++;
      if(o instanceof Pug)
        ((Counter)h.get(
          "class c11.petcount2.Pug")).i++;
      if(o instanceof Cat)
        ((Counter)h.get(
          "class c11.petcount2.Cat")).i++;
      if(o instanceof Rodent)
        ((Counter)h.get(
          "class c11.petcount2.Rodent")).i++;
      if(o instanceof Gerbil)
        ((Counter)h.get(
          "class c11.petcount2.Gerbil")).i++;
      if(o instanceof Hamster)
        ((Counter)h.get(
          "class c11.petcount2.Hamster")).i++;
    }
    for(int i = 0; i < pets.size(); i++)
      System.out.println(
        pets.elementAt(i).getClass().toString());
    Enumeration keys = h.keys();
    while(keys.hasMoreElements()) {
      String nm = (String)keys.nextElement();
      Counter cnt = (Counter)h.get(nm);
      System.out.println(
        nm.substring(nm.lastIndexOf('.') + 1) +
        " quantity: " + cnt.i);
    }
  }
} ///:~ 

Here,


the


typenames

array has been removed in favor of getting the type name strings from the


Class

object. Notice the extra work for this: the class name is not, for example,


Gerbil,

but instead


c11.petcount2.Gerbil

since the package name is included. Notice also that the system can distinguish


between classes and interfaces.

You


can also see that the creation of


petTypes

does not need to be surrounded by a


try

block since it’s evaluated at compile time and thus won’t throw any


exceptions, unlike


Class.forName( )

.

When


the


Pet

objects are dynamically created, you can see that the random number is


restricted so it is between 1 and


petTypes.length

and does not include zero. That’s because zero refers to


Pet.class

,


and presumably a generic


Pet

object is not interesting. However, since


Pet.class

is part of


petTypes

the result is that all of the pets get counted.


A
dynamic instanceof

Java


1.1


has added the
isInstance
method to the class
Class.
This allows you to dynamically call the
instanceof
operator, which you could do only statically in Java 1.0

(as previously shown). Thus, all those tedious
instanceof
statements can be removed in the
PetCount
example:
//: PetCount3.java
// Using Java 1.1 isInstance()
package c11.petcount3;
import java.util.*;
 
class Pet {}
class Dog extends Pet {}
class Pug extends Dog {}
class Cat extends Pet {}
class Rodent extends Pet {}
class Gerbil extends Rodent {}
class Hamster extends Rodent {}
 
class Counter { int i; }
 
public class PetCount3 {
  public static void main(String[] args) {
    Vector pets = new Vector();
    Class[] petTypes = {
      Pet.class,
      Dog.class,
      Pug.class,
      Cat.class,
      Rodent.class,
      Gerbil.class,
      Hamster.class,
    };
    try {
      for(int i = 0; i < 15; i++) {
        // Offset by one to eliminate Pet.class:
        int rnd = 1 + (int)(
          Math.random() * (petTypes.length - 1));
        pets.addElement(
          petTypes[rnd].newInstance());
      }
    } catch(InstantiationException e) {}
      catch(IllegalAccessException e) {}
    Hashtable h = new Hashtable();
    for(int i = 0; i < petTypes.length; i++)
      h.put(petTypes[i].toString(),
        new Counter());
    for(int i = 0; i < pets.size(); i++) {
      Object o = pets.elementAt(i);
      // Using isInstance to eliminate individual
      // instanceof expressions:
      for (int j = 0; j < petTypes.length; ++j)
        if (petTypes[j].isInstance(o)) {
          String key = petTypes[j].toString();
          ((Counter)h.get(key)).i++;
        }
    }
    for(int i = 0; i < pets.size(); i++)
      System.out.println(
        pets.elementAt(i).getClass().toString());
    Enumeration keys = h.keys();
    while(keys.hasMoreElements()) {
      String nm = (String)keys.nextElement();
      Counter cnt = (Counter)h.get(nm);
      System.out.println(
        nm.substring(nm.lastIndexOf('.') + 1) +
        " quantity: " + cnt.i);
    }
  }
} ///:~ 

You


can see that the Java 1.1


isInstance( )
method has eliminated the need for the
instanceof
expressions. In addition, this means that you can add new types of pets simply
by changing the
petTypes
array; the rest of the program does not need modification (as it did when using
the
instanceof
expressions).
Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.