Skip to main content

Reflection in Action

February 12, 2008

{cs.r.title}







Have you ever wondered how an IDE lists all your class details,
including the private fields and methods? Or how IDEs are also able
to list the classes (together with their details) archived within
JAR files that include no source?

These are examples of reflection.

Together with listing the contents of an arbitrary class, this
article will illustrate how reflection can be used to leverage
programming and push it to a higher level of abstraction. We will
start from very basic examples and move forth by applying
reflection within a simple application.

What Is Reflection?

Reflection is a mechanism that enables dynamic discovery and
binding of classes, methods, fields, and all the other elements the
language is made of. Reflection can do more than just simply list
classes, fields, and methods. Through reflection, we can actually
create instances, invoke methods, and access fields if need be.

Most programmers have used dynamic class loading when loading
their JDBC drivers, using something similar to the following code
fragment, where an instance of the MySQL JDBC driver is being
dynamically loaded:

[prettify]
Class.forName("com.mysql.jdbc.Driver").newInstance();
[/prettify]

Why and When Should Reflection Be Used?

Reflection provides a higher level of abstraction. In other
words, reflection allows us to examine the object at hand and act
accordingly during runtime. For example, imagine you have to
perform the same task, such as searching for an instance, on more than
one kind of object. You can either write some code for every
different kind of object, or you can use reflection. As you may
already have realized, reflection can minimize maintenance as the
same code, since by using reflection, your instance-searching code
will work with other classes. We will come to this example later
on. I have included it here just to show you how reflection can be
used to our advantage.

Dynamic Discovery

Let's start by discovering a class's contents and listing its
constructors, fields and methods. This is not that useful, but it is
essential to grasp the Reflection API and its potential.

Create the Product class illustrated below. All our
examples are saved under the same package, called
ria.

[prettify]
package ria;

public class Product {
  private String description;

  private long id;

  private String name;

  private double price;

  //Getters and setters are omitted for shortness
}
[/prettify]

With the Product class ready, we can move on and
create the second class, called ReflectionUtil, which
will list the first class's (Product) details. As you
may have anticipated, this class will include utility methods that
will perform all the reflection functionality required within the
application. For the time being, this class will only include one
method, describeInstance(Object), with one parameter
of the type Object.

The code for the ReflectionUtil class is
illustrated in the following listing.

[prettify]
package ria;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionUtil {

  public static void describeInstance(Object object) {
    Class<?> clazz = object.getClass();

    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    Field[] fields = clazz.getDeclaredFields();
    Method[] methods = clazz.getDeclaredMethods();

    System.out.println("Description for class: " + clazz.getName());
    System.out.println();
    System.out.println("Summary");
    System.out.println("-----------------------------------------");
    System.out.println("Constructors: " + (constructors.length));
    System.out.println("Fields: " + (fields.length));
    System.out.println("Methods: " + (methods.length));

    System.out.println();
    System.out.println();
    System.out.println("Details");
    System.out.println("-----------------------------------------");

    if (constructors.length > 0) {
      System.out.println();
      System.out.println("Constructors:");
      for (Constructor<?> constructor : constructors) {
        System.out.println(constructor);
      }
    }

    if (fields.length > 0) {
      System.out.println();
      System.out.println("Fields:");
      for (Field field : fields) {
        System.out.println(field);
      }
    }

    if (methods.length > 0) {
      System.out.println();
      System.out.println("Methods:");
      for (Method method : methods) {
        System.out.println(method);
      }
    }
  }
}
[/prettify]

Java includes a set of reflection-related classes packaged under
the Reflection API. The classes Constructor,
Field, and Method are some of the classes
belonging to this package. Like the well known Class
class, these are used by Java to represent the programs we write as
objects. In order to describe an object, we need to know what it is
made of. Where do we start? We start from the class, as it
contains all of our code.

[prettify]
Class<?> clazz = object.getClass();
[/prettify]

Notice the generic declaration Class.
Generics, in a nutshell, provide type-safe operations by ensuring
that a given instance is of the specified type. Our method
(describeInstance(Object)) is not bound to a specific
type and is designed to work with any given object. Thus the
unbounded wildcard, , is used instead.

The Class class has a number of methods. We'll
focus on some that are relevant to us. These methods are
illustrated in the following code fragment.

[prettify]
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
Field[] fields = clazz.getDeclaredFields();
Method[] methods = clazz.getDeclaredMethods();
[/prettify]

The above Class methods return arrays of
constructors, fields, and the methods that this object is made
from.

Note that the Class class includes two sets of
getter methods: one set includes the declared word in
their names, and the other set does not. The difference is that
getDeclaredMethods() will return all methods that
belong to the class, while getMethods() returns
only the public ones. It's also important to
understand that only methods declared within the class are
returned. Inherited methods are not retrieved.

It's important to understand that the
ReflectionUtil class doesn't have a reference to the
Product class. We need another class that creates an
instance of the product details class and prints its details.

[prettify]
package ria;

public class Main {
  public static void main(String[] args) throws Exception {
    Product product = new Product();
    product.setId(300);
    product.setName("My Java Product Name");
    product.setDescription("My Java Product description...");
    product.setPrice(10.10);

    ReflectionUtil.describeInstance(product);
  }
}
[/prettify]

The above class should produce the following output (or
something similar):

[prettify]
Description for class: ria.Product

Summary
-----------------------------------------
Constructors: 1
Fields:       4
Methods:      8


Details
-----------------------------------------

Constructors:
public ria.Product()

Fields:
private java.lang.String ria.Product.description
private long ria.Product.id
private java.lang.String ria.Product.name
private double ria.Product.price

Methods:
public java.lang.String ria.Product.getName()
public long ria.Product.getId()
public void ria.Product.setName(java.lang.String)
public void ria.Product.setId(long)
public void ria.Product.setDescription(java.lang.String)
public void ria.Product.setPrice(double)
public java.lang.String ria.Product.getDescription()
public double ria.Product.getPrice()
[/prettify]

For this method to be more useful, it should also print the
values of the instance being described together with the class
details. The Field class includes a method called
get(Object) that returns the value of the field for
the given instance.

For example, let's take our Product class. The
class has four instance variables. The retrieved values are
dependent on the instance, as different instances may have
different values. Thus the instance must be provided to the
Field in order to return its value as shown below:

[prettify]
field.get(object)
[/prettify]

where field is an instance of Field
and the object is an instance of any Java class.

Before we hastily start adding any code, we must appreciate the
fact that the fields of our class have the private
access modifier. An exception is thrown if we invoke the
get(Object) method as it is. We need to invoke the
method setAccessible(boolean) for the
Field class and pass true as the
parameter before attempting to access the field's value.

[prettify]
field.setAccessible(true);
[/prettify]

Now that we know all the tricks involved in getting a field's
value, we can add the following code at the bottom of the
decribeInstance(Object) method.

[prettify]
if (fields.length > 0) {
  System.out.println();
  System.out.println();
  System.out.println("Fields' values");
  System.out.println("-----------------------------------------");
  for (Field field : fields) {
    System.out.print(field.getName());
    System.out.print(" = ");
    try {
      field.setAccessible(true);
      System.out.println(field.get(object));
    } catch (IllegalAccessException e) {
      System.out.println("(Exception Thrown: " + e + ")");
    }
  }
}
[/prettify]

To show you its effectiveness, I'm going to create an instance
of the java.awt.Rectangle class and print its details
using the describeInstance(Object) method.

[prettify]
Rectangle rectangle = new Rectangle(1, 2, 100, 200);
ReflectionUtil.describeInstance(rectangle);
[/prettify]

The above code fragment should produce something similar to the
following. Note that some of the output is truncated, as it's too
long to display.

[prettify]
Description for class: java.awt.Rectangle

Summary
-----------------------------------------
Constructors: 7
Fields:       5
Methods:      39


Details
-----------------------------------------

Constructors:
public java.awt.Rectangle()
public java.awt.Rectangle(java.awt.Rectangle)
public java.awt.Rectangle(int,int,int,int)
public java.awt.Rectangle(int,int)
public java.awt.Rectangle(java.awt.Point,java.awt.Dimension)
public java.awt.Rectangle(java.awt.Point)
public java.awt.Rectangle(java.awt.Dimension)

Fields:
public int java.awt.Rectangle.x
public int java.awt.Rectangle.y
public int java.awt.Rectangle.width
public int java.awt.Rectangle.height
private static final long java.awt.Rectangle.serialVersionUID

Methods:
public void java.awt.Rectangle.add(int,int)
public void java.awt.Rectangle.add(java.awt.Point)
public void java.awt.Rectangle.add(java.awt.Rectangle)
public boolean java.awt.Rectangle.equals(java.lang.Object)
public java.lang.String java.awt.Rectangle.toString()
public boolean java.awt.Rectangle.contains(int,int,int,int)
public boolean java.awt.Rectangle.contains(java.awt.Rectangle)
public boolean java.awt.Rectangle.contains(int,int)
public boolean java.awt.Rectangle.contains(java.awt.Point)
public boolean java.awt.Rectangle.isEmpty()
public java.awt.Point java.awt.Rectangle.getLocation()
public java.awt.Dimension java.awt.Rectangle.getSize()
public void java.awt.Rectangle.setSize(java.awt.Dimension)
public void java.awt.Rectangle.setSize(int,int)
public void java.awt.Rectangle.resize(int,int)
private static native void java.awt.Rectangle.initIDs()
public void java.awt.Rectangle.grow(int,int)
public boolean java.awt.Rectangle.intersects(java.awt.Rectangle)
private static int java.awt.Rectangle.clip(double,boolean)
public java.awt.geom.Rectangle2D java.awt.Rectangle.createIntersection(java....
public java.awt.geom.Rectangle2D java.awt.Rectangle.createUnion(java.awt.geo...
public java.awt.Rectangle java.awt.Rectangle.getBounds()
public java.awt.geom.Rectangle2D java.awt.Rectangle.getBounds2D()
public double java.awt.Rectangle.getHeight()
public double java.awt.Rectangle.getWidth()
public double java.awt.Rectangle.getX()
public double java.awt.Rectangle.getY()
public boolean java.awt.Rectangle.inside(int,int)
public java.awt.Rectangle java.awt.Rectangle.intersection(java.awt.Rectangle)
public void java.awt.Rectangle.move(int,int)
public int java.awt.Rectangle.outcode(double,double)
public void java.awt.Rectangle.reshape(int,int,int,int)
public void java.awt.Rectangle.setBounds(int,int,int,int)
public void java.awt.Rectangle.setBounds(java.awt.Rectangle)
public void java.awt.Rectangle.setLocation(java.awt.Point)
public void java.awt.Rectangle.setLocation(int,int)
public void java.awt.Rectangle.setRect(double,double,double,double)
public void java.awt.Rectangle.translate(int,int)
public java.awt.Rectangle java.awt.Rectangle.union(java.awt.Rectangle)


Fields' values
-----------------------------------------
x = 1
y = 2
width = 100
height = 200
serialVersionUID = -4345857070255674764
[/prettify]

Create a New Instance Using Reflection

Reflection can also be used to create instances of a new object.
There are cases, such as when dynamically loading a JDBC driver as
illustrated previously, where an instance of the object needs to be
created dynamically. Furthermore, we can use the
Constructor class to create new instances, especially
for instances that require parameters during their instantiation.
The following two overloaded methods can be added to our
ReflectionUtil class.

[prettify]
public static <T> T newInstance(Class<T> clazz)
    throws IllegalArgumentException, SecurityException,
      InstantiationException, IllegalAccessException,
      InvocationTargetException, NoSuchMethodException {
  return newInstance(clazz, new Class[0], new Object[0]);
}

public static <T> T newInstance(Class<T> clazz, Class<?>[] paramClazzes,
      Object[] params) throws IllegalArgumentException,
        SecurityException, InstantiationException, IllegalAccessException,
        InvocationTargetException, NoSuchMethodException {

    return clazz.getConstructor(paramClazzes).newInstance(params);
}
[/prettify]

Note that the newInstance(Object[]) will throw
exceptions if the constructor arguments supplied are not adequate.
The class being instantiated must contain a constructor with the
given signature.

The first method (newInstance(Class)) can
be used to instantiate an object from any class having a default
constructor. Alternatively, the second method can be used. By
passing the parameter types and their values in the respective
parameters, the instantiation will happen through the matching
constructor. For example, the Rectangle class can be
instantiated using the constructor with four parameters of the type
int, using the following code:

[prettify]
Object[] params = { 1, 2, 100, 200 };
Class[] paramClazzes = { int.class, int.class, int.class, int.class };
Rectangle rectangle = ReflectionUtil.newInstance(
                               Rectangle.class, paramClazzes, params);
System.out.println(rectangle);
[/prettify]

The above will produce the following output.

[prettify]
java.awt.Rectangle[x=1,y=2,width=100,height=200]
[/prettify]

Changing the Fields's Values Through Reflection

The fields's values can be set through reflection in a fashion
similar to how they are read. It is import to set the field's
accessibility before trying to set the value, as otherwise an
exception is thrown.

[prettify]field.setAccessible(true);
field.set(object, newValue);
[/prettify]

We can easily draft a method that can set the value of any
object, as shown in the following example.

[prettify]
public static void setFieldValue(Object object, String fieldName,
      Object newValue) throws NoSuchFieldException,
      IllegalArgumentException, IllegalAccessException {
  Class<?> clazz = object.getClass();
  Field field = clazz.getDeclaredField(fieldName);
  field.setAccessible(true);
  field.set(object, newValue);
}
[/prettify]

This method has a pitfall, as it only retrieves fields from the
given class; inherited fields are not included. This can quickly be
fixed using the following method, which looks up the object
hierarchy for the required Field.

[prettify]
public static Field getDeclaredField(Object object, String name)
      throws NoSuchFieldException {
  Field field = null;
  Class<?> clazz = object.getClass();
  do {
    try {
      field = clazz.getDeclaredField(name);
    } catch (Exception e) { }
  } while (field == null & (clazz = clazz.getSuperclass()) != null);

  if (field == null) {
    throw new NoSuchFieldException();
  }

  return field;
}
[/prettify]

This method will return the Field with the given
name, if found; otherwise it will throw an exception to indicate that
this object neither has nor inherits this field. It starts
searching from the given class and works its way up the hierarchy
until either the Field is found, or no superclass is
available.

Note that all Java classes inherit (directly or transitively)
from the Object class. As you may have realized, the
Object class does not inherit from itself. Thus the
Object class does not have a superclass.

The method setFieldValue(Object, String, Object)
illustrated previously is modified to cater for this situation. The
changes are shown in bold below.

[prettify]
public static void setFieldValue(Object object, String fieldName,
    Object newValue) throws IllegalArgumentException,
    IllegalAccessException, NoSuchFieldException {

  Field field = getDeclaredField(object, fieldName);
  field.setAccessible(true);
  field.set(object, newValue);
}
[/prettify]

Let's create another class called Book that
extends the Product class discussed earlier, and apply
what we've learned so far.

[prettify]
package ria;

public class Book extends Product {
  private String isbn;

  //Getters and setters are omitted for shortness
}
[/prettify]

Now let's set the book's id using the
setFieldValue(Object, String, Object) method.

[prettify]
Book book = new Book();
ReflectionUtil.setFieldValue(book, "id", 1234L);
System.out.println(book.getId());
[/prettify]

The above will produce the following output:
1234.

Invoking Methods Through Reflection

As you may already have assumed, the invocation of methods is
very similar to creating new instances and accessing fields
discussed above.

As far as reflection is concerned, all methods have parameters
and return a value. This may sound weird, but it's true. Let's
analyze the following method:

[prettify]
public void doNothing(){
  // This method doesn't do anything
}
[/prettify]

This method has a return type, of the type void, and an
empty parameter list. It can be invoked through reflection in the
following manner.

[prettify]Class<?> clazz = object.getClass();
Method method = Clazz.getDeclaredMethod("doNothing");
method.invoke(object, new Object[0]);
[/prettify]

The invoke method, from the Method
class, requires two arguments: the instance on which the method
will be invoked, and the list of parameters as an array of objects.
Note that the method doNothing() has no parameters.
Despite that, we still need to specify the arguments as an empty
array of objects.

A method also has a return type; void, in our case.
The return value, if any, can be saved as an Object
something similar to the following example.

[prettify]
Object returnValue = method.invoke(object, new Object[0]);
[/prettify]

In this case, the return value is null as this
method does not return anything. Note that methods can return a
null on purpose and this may be a little
confusing.

Before we finalize this section, it's important to understand
that a method can be inherited in the same way that fields are. We
can have another utility method to retrieve the method within the
hierarchy rather than just from the class at hand.

[prettify]
public static Method getDeclaredMethod(Object object, String name)
    throws NoSuchMethodException {
  Method method = null;
  Class<?> clazz = object.getClass();
  do {
    try {
      method = clazz.getDeclaredMethod(name);
    } catch (Exception e) { }
  } while (method == null & (clazz = clazz.getSuperclass()) != null);

  if (method == null) {
    throw new NoSuchMethodException();
  }

  return method;
}
[/prettify]

Finally, the generic invoke method is listed below.
Note again the methods may be private and thus it's
ideal to set their accessibility before invoking them.

[prettify]
public static Object invokeMethod(Object object, String methodName,
    Object[] arguments) throws NoSuchMethodException,
      IllegalArgumentException, IllegalAccessException,
      InvocationTargetException {
  Method method = getDeclaredMethod(object, methodName);
  method.setAccessible(true);
  return method.invoke(object, arguments);
}
[/prettify]

Apart from executing private methods, invocation of
methods through reflection can be useful to expose functionality
and easily change the execution flow during runtime.

Reflection Within an Application

Until now, we have only created utility methods and tried out
simple examples. Real-life programming requires more than that.
Imagine that we need to search through our objects and determine whether
a given object matches some criteria or not. The first option is to
write an interface and implement it in every object that returns
true if this instance matches the criteria,
false otherwise. Unfortunately, this approach requires
us to implement a method within every class we have. New
classes will have to implement this interface and provide a body
for its abstract method. Alternatively, we can use reflection to
retrieve the object's fields and check whether their values meet
the criteria.

Let us first create another method that returns the object's
fields. Remember that there's no built-in method that returns all
the fields including the inherited ones. Thus we need to retrieve
them ourselves by extracting them set by set until we reach the top
of the hierarchy. This method can be added to the
ReflectionUtil class.

[prettify]
public static List <Field> getDeclaredFields(Class clazz) {
  List<Field> fields = new ArrayList<Field>();
  do {
    try {
      fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
    } catch (Exception e) { }
  } while ((clazz = clazz.getSuperclass()) != null);
  return fields;
}
[/prettify]

Now we only need to match their string values with the given
criteria, as shown in the following code fragment. The
String method valueOf(Object) is used to
convert the fields's values into strings without returning
null or throwing any exceptions. Note that this may
not always work for complex data types.

[prettify]
public static boolean search(Object object, String criteria)
    throws IllegalArgumentException, IllegalAccessException {
  List <Field> fields = ReflectionUtil.getDeclaredFields(object.getClass());
  for (Field field : fields) {
    field.setAccessible(true);
    if (String.valueOf(field.get(object)).equalsIgnoreCase(criteria)) {
      return true;
    }
  }

  return false;
}
[/prettify]

Let's create a new class called Address and try
this out. The code for this class is shown below.

[prettify]
package ria;

public class Address {
  private String country;

  private String county;

  private String street;

  private String town;

  private String unit;

  //Getters and setters are omitted for shortness
}
[/prettify]

Now let's create an instance of both the Book and
Address classes and apply our search method.

[prettify]
Book book = new Book();
book.setId(200);
book.setName("Reflection in Action");
book.setIsbn("123456789-X");
book.setDescription("An article about reflection");

Address address = new Address();
address.setUnit("1");
address.setStreet("Republic Street");
address.setTown("Valletta");
address.setCountry("Malta");

System.out.println("Book match? " + search(book, "Valletta"));
System.out.println("Address match? " + search(address, "Valletta"));
[/prettify]

The first match (the one against the book instance) will return
false, while the address instance will return
true. The search method can be applied against any
object without having to add or implement anything.

Drawbacks of Reflection

Until now we've only talked about how good reflection is and how it
can make life easier. Unfortunately, everything comes with a price.
Reflection is very powerful and provides a great deal of
flexibility, but we should not start programming everything using
reflection. If possible, you may want avoid using reflection in
some cases as it introduces the following drawbacks: performance
overhead, security restrictions, and exposure of hidden
members.

Sometimes logic is preserved through access modifiers. The
following code fragment is a clear example:

[prettify]
public class Student {
  private String name;

  public Student(String name){
    this.name = name;
  }
}
[/prettify]

The student's name can only be changed through the constructor,
when the object is initialized. Reflection allows you to set the
student's name to any String even after the object is
initialized. As you can see, this disrupts the business logic and
may cause the program to behave in an unpredictable way.

The Java compiler, like most other compilers, tries to optimize
the code as much as possible. This is not possible with reflection
as the types are resolved at runtime, while the compiler works at
compile time. Furthermore, the type must be resolved at a later
stage, which is at runtime.

Conclusion

Reflection can be used to apply the same logic--search, for
example--against different objects without having to implement a
new version of the code for every new type. This also centralizes
the logic in one place. It unfortunately has drawbacks in that it
can increase the code complexity. Performance is another side
effect of reflection as optimizations cannot be performed on such
code.

Resources


width="1" height="1" border="0" alt=" " />
Albert Attard teaches Java among other programming languages, at a local ICT college, both at introductory and intermediate levels.
Related Topics >> Programming   |