You are on page 1of 12

Run Time Type Identification (RTTI)

and Reflection

What is RTTI?

By Run Time Type Identification (RTTI) we


denote a mechanism to find the type of an
object at run time.

Typically, there are two situations when the RTTI


mechanism is used:
• downcasting to a child class
• checking an object type via instanceof

1
Downcasting

Assume we have the following hierarchy:

shape // upcasting - checked at compile time:


Shape circle = new Circle();
draw() Shape square = new Square();
// use of polymorphism:
circle.draw();
square.draw();
circle square // downcasting – checked at run time
((Square)square).getCorners();

getCorners()

Downcasting is checked at run-time: if invalid


the JVM throws a ClassCastException

Checking an object type (I)

The keyword instanceof tells you if an object is


an instance of a particular type.
// safe downcast
if (x instanceof Square)
((Square)x).getCorners();

Both downcasting and type checking is


implemented in Java using the Class class.

2
The Class class.

For each class used in a program, Java stores its


information in a Class object:

• At compile time, Java creates the Class


objects and stores them in .class files.
• At run time, when an object of a class is
created for the first time, the JVM loads the
Class object into memory.
• When the Class object of a given type is
loaded, it is used to create any object of that
type.

Accessing Class objects (I).

Class objects can be accessed from Java


programs in different ways:

// static method of Class (at run time):


Class circle_class = Class.forName(“Circle“);
// class literal (at compile time):
Class square_class = Square.class;
// method of Object (at run time):
Class x_class = x.getClass();

Class literals are more efficient since they are


checked at compile time.
With getClass() you have dynamic access to
the unknown type of an object.

3
Accessing Class objects (II).

Superclasses can also be identified:

// method of Object (at run time) to get the parent class:


Class x_super = x.getSuperclass();

• getSuperclass() returns null if the class has


no superclass
• Since Java does not support multiple
inheritance, the superclass is always unique
• To walk through the inheritance tree, call
getSuperclass() recursively

Accessing Class objects (III).

Finally, interfaces are also represented by Class


objects:
// method of Object (at run time) to get the interfaces:
Class[] x_ifaces = x.getInterfaces();
// prints out all interfaces names:
for (int i = 0; i < x_ifaces.length; i++) {
String ifaceName = x_ifaces[i].getName();
System.out.println(ifaceName);
}

To check if a Class object is an interface, use


the isInterface() method.

4
An example of RTTI.

We first build an array of Class objects,


obtained via the class literals:

public class ShapeCount {


private static Random rand = new Random();
public static void main(String[] args) {
Object[] shapes = new Object[15];
Class[] shapeTypes = {
Shape.class,
Circle.class,
Square.class,
};
// ..
// continues in the next slide
// ...

An example of RTTI.

Then we randomly generate objects of different


classes with newInstance() and show the use
of isInstance() :

// main() method,
// continues from the previous slide
try {
for(int i = 0; i < shapes.length; i++) {
int rnd = 1 + rand.nextInt(shapeTypes.length - 1);
shapes[i] = shapeTypes[rnd].newInstance();
shapeTypes[rnd].isInstance(shapes[i]); // always true!
}
} catch(InstantiationException e) {
System.out.println("Cannot instantiate");
System.exit(1);
} } }

5
An example of RTTI.

Note that newInstance() can generate an


InstantiationException, which must be
caught.

// main() method,
// continues from the previous slide
try {
for(int i = 0; i < shapes.length; i++) {
int rnd = 1 + rand.nextInt(shapeTypes.length - 1);
shapes[i] = shapeTypes[rnd].newInstance();
shapeTypes[rnd].isInstance(shapes[i]);
}
} catch(InstantiationException e) {
System.out.println("Cannot instantiate");
System.exit(1);
} } }

Checking an object type (II)

instanceof and isInstance() are mostly


equivalent, but the former needs a named
class. From the previous example:
// the following does not compile:
shapes[i] instanceof shapeTypes[rnd];
// but these do:
shapes[i] instanceof Shape;
shapes[i] instanceof Circle;
shapes[i] instanceof Square;

A (bad) alternative is directly comparing Class


objects (but it does not handle inheritance):
Shape circle = new Circle();
circle.getClass() == Circle.class; // true
circle.getClass() == Shape.class; // false
circle instanceof Shape; //true
Shape.isinstance(circle); //true

6
Class Names

Every class has a unique name, which you define


when declaring a class with the class
keyword.
The fully qualified class name can be accessed
via the getName() method of a Class object:

// gets the class of the object:


Class c = x.getClass();
// prints the fully qualified class name:
System.out.println(c.getName());
// e.g. if x is a Button object it prints:
java.awt.Button

RTTI limitation
The Java RTTI mechanism allows you to discover
an objects type at run time. However:
it requires full knowledge of the needed
types at compile time.

There are situations when this knowledge is not available:


• Components based programming
• Distributed computing

Java provides a mechanism, called Reflection,


to overcome RTTI limitation.

7
Reflection

• The reflection mechanism allows to detect the


available methods and fields of a class
completely at run time.
• It is supported by the Class objects and the
java.lang.reflect library.
• It works by loading the appropriate Class
object from a .class file, which must be
available at run time (not compile time),
either locally or through the network.

java.lang.reflect overview

The library provides Field, Method and


Constructor classes, all implementing the
Member interface.
When the JVM creates a Class object, it also
creates corresponding objects for all its
members.

All this objects are available at run time, and can


be used to read and modify class fields, invoke
the class methods and instantiate objects of
the class, without any information available at
compile time.

8
An example of reflection.

As an example of using reflection, we show a


simple program which lists all the methods of
a given class.
The class name is provided as argument:
public class ShowMethods {
private static final String usage = "usage: \n" +
"ShowMethods qualified.class.name\n" +
"To show all methods in class";
public static void main(String[] args) {
if(args.length < 1) {
System.out.println(usage);
System.exit(0);
}
// main() continues on the next slide

An example of reflection.

Using the Class object, we can obtain arrays of


all the methods and constructors provided by
the class, represented as Method and
Constructor objects:
// main()
// it continues from the next slide
try {
Class c = Class.forName(args[0]); // JVM loads the class
Method[] m = c.getMethods(); // array of all methods
Constructor[] ctor = c.getConstructors(); // array of all ctors
for(int i = 0; i < m.length; i++)
System.out.println( m[i].toString());
for(int i = 0; i < ctor.length; i++)
System.out.println( ctor[i].toString() );
} catch(ClassNotFoundException e) {
System.out.println("No such class: " + e);
} } }

9
Discovering modifiers.

Classes can be defined with modifiers: public,


abstract or final.
Reflection can also provide classes modifiers:

Class c = x.getClass();
// returns the modifiers flags:
int m = c.getModifiers();
// they have to be checked with the following methods:
Modifier.isPublic(m);
Modifier.isAbstract(m);
Modifier.isFinal(m);

Discovering fields.

The method getFields() returns an array of


Field objects, one for each public field
member of either:
• This class
• Its superclass
• The implemented interfaces and their ancestors

Class c = x.getClass();
Fields[] fields = c.getFields();
for (int i = 0; i < fields.length; i++) {
// gets the name:
String name = fields[i].getName();
// gets the type:
Class type = fields[i].getType();
}

10
Invoking constructors.
The newInstance() method of Class, works
through reflection, and can be used also in
case a class is available only at run time.
However, it only works for constructors without
arguments. Otherwise a Constructor object
has to be used. E.g:
// NOTE: exception handling has been omitted for brevity!
// assume Square has the ctor Square(int size)
// first we get the constructor with specified arguments:
Class[] argsClass = new Class[] {int.class};
Integer size = new Integer(10);
Constructor ctor = Square.class.getConstructor(argsClass);
// then we invokes the constructor with arguments:
Object[] args = new Object[] {size};
Object o = ctor.newInstance(args);

Invoking methods.

Invoking methods is similar to invoking


constructors with arguments.
In the following example we assume that class
Square has a method:
Square doSomething(Square other);

// NOTE: exception handling has been omitted for brevity!


// first we get the method with specified name and parameters:
Class[] parmsClass = new Class[] {Square.class};
Method method = Square.class.getMethod(“doSomething“, parmsClass);
// then we invokes the method, on the specified object
// and with the specified parameters:
Square one = new Square();
Square other = new Square();
Square result = (Square) method.invoke(one, other);

11
Conclusions.

RTTI:
• It requires type information (.class files) at
compile time
Reflection:
• It requires type information at run time

Reflection is rarely used directly, but used by


Java to provide, eg., serialization and
JavaBeans.

References

The lecture and the examples are based on


chapter 10 of Thinking in Java, 3rd Edition, by
Bruce Eckel.

The book is freely available at


http://www.mindview.net/Books/TIJ/
together with the source code of the examples.

Also available on the net is Sun’s tutorial on Reflection:


http://java.sun.com/docs/books/tutorial/reflect/

12

You might also like