Team LiB
Previous Section Next Section

5.13. Types, Reflection, and Dynamic Loading

The java.lang.Class class represents data types in Java and, along with the classes in the java.lang.reflect package, gives Java programs the capability of introspection (or self-reflection); a Java class can look at itself, or any other class, and determine its superclass, what methods it defines, and so on.

5.13.1. Class Objects

You can obtain a Class object in Java in several ways:

// Obtain the Class of an arbitrary object o
Class c = o.getClass();

// Obtain a Class object for primitive types with various predefined constants
c = Void.TYPE;          // The special "no-return-value" type
c = Byte.TYPE;          // Class object that represents a byte
c = Integer.TYPE;       // Class object that represents an int
c = Double.TYPE;        // etc; see also Short, Character, Long, Float 

// Express a class literal as a type name followed by ".class"
c = int.class;          // Same as Integer.TYPE
c = String.class;       // Same as "dummystring".getClass()
c = byte[].class;       // Type of byte arrays
c = Class[][].class;    // Type of array of arrays of Class objects

5.13.2. Reflecting on a Class

Once you have a Class object, you can perform some interesting reflective operations with it:

import java.lang.reflect.*;

Object o;                   // Some unknown object to investigate
Class c = o.getClass();     // Get its type

// If it is an array, figure out its base type
while (c.isArray()) c = c.getComponentType();

// If c is not a primitive type, print its class hierarchy
if (!c.isPrimitive()) {
  for(Class s = c; s != null; s = s.getSuperclass()) 
    System.out.println(s.getName() + " extends");
}

// Try to create a new instance of c; this requires a no-arg constructor
Object newobj = null;
try { newobj = c.newInstance(); }
catch (Exception e) { 
  // Handle InstantiationException, IllegalAccessException
}

// See if the class has a method named setText that takes a single String
// If so, call it with a string argument
try {
    Method m = c.getMethod("setText", new Class[] { String.class });
    m.invoke(newobj, new Object[] { "My Label" });
} catch(Exception e) { /* Handle exceptions here */ }

// These are varargs methods in Java 5.0 so the syntax is much cleaner.
// Look for and invoke a method named "put" that takes two Object arguments
try {
    Method m = c.getMethod("add", Object.class, Object.class);
    m.invoke(newobj, "key", "value");
} catch(Exception e) { System.out.println(e); }

// In Java 5.0 we can use reflection on enumerated types and constants
Class<Thread.State> ts = Thread.State.class;  // Thread.State type
if (ts.isEnum()) {  // If it is an enumerated type
    Thread.State[] constants = ts.getEnumConstants(); // get its constants
}
try {
    Field f = ts.getField("RUNNABLE");       // Get the field named "RUNNABLE"
    System.out.println(f.isEnumConstant());  // Is it an enumerated constant?
}
catch(Exception e) { System.out.println(e); }

// The VM discards generic type information at runtime, but it is stored
// in the class file for the compiler and is accessible through reflection
try { 
    Class map = Class.forName("java.util.Map");

    TypeVariable<?>[] typevars = map.getTypeParameters();
    for(TypeVariable<?> typevar : typevars) {
        System.out.print(typevar.getName());
        Type[] bounds = typevar.getBounds();
        if (bounds.length > 0) System.out.print(" extends ");
        for(int i = 0; i < bounds.length; i++) {
            if (i > 0) System.out.print(" & ");
            System.out.print(bounds[i]);
        }
        System.out.println();
    }
}
catch(Exception e) { System.out.println(e); }

// In Java 5.0, reflection can also be used on annotation types and to
// determine the values of runtime visible annotations
Class<?> a = Override.class;  // an annotation class
if (a.isAnnotation()) {       // is this an annotation type?
    // Look for some meta-annotations
    java.lang.annotation.Retention retention =
        a.getAnnotation(java.lang.annotation.Retention.class);
    if (retention != null) 
        System.out.printf("Retention: %s%n", retention.value());
}

5.13.3. Dynamic Class Loading

Class also provides a simple mechanism for dynamic class loading in Java. For more complete control over dynamic class loading, however, you should use a java.lang.ClassLoader object, typically a java.net.URLClassLoader. This technique is useful, for example, when you want to load a class that is named in a configuration file instead of being hardcoded into your program:

// Dynamically load a class specified by name in a config file
String classname =                     // Look up the name of the class
    config.getProperty("filterclass",  // The property name
                       "com.davidflanagan.filters.Default"); // A default

try {
  Class c = Class.forName(classname);  // Dynamically load the class
  Object o = c.newInstance();          // Dynamically instantiate it
} catch (Exception e) { /* Handle exceptions */ }

The preceding code works only if the class to be loaded is in the class path. If this is not the case, you can create a custom ClassLoader object to load a class from a path (or URL) you specify yourself:

import java.net.*;
String classdir = config.getProperty("filterDirectory"); // Look up class path
try {
  ClassLoader loader = new URLClassLoader(new URL[] { new URL(classdir) });
  Class c = loader.loadClass(classname);
}
catch (Exception e) { /* Handle exceptions */ }

5.13.4. Dynamic Proxies

The Proxy class and InvocationHandler interface to the java.lang.reflect package were added to Java 1.3. Proxy is a powerful but infrequently used class that allows you to dynamically create a new class or instance that implements a specified interface or set of interfaces. It also dispatches invocations of the interface methods to an InvocationHandler object.

    Team LiB
    Previous Section Next Section