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.
|