5.13. Types, Reflection, and Dynamic LoadingThe 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 ObjectsYou 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 ClassOnce 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 LoadingClass 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 ProxiesThe 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. |