Table of Contents
The Java Reflection API allows a program to inspect and discover information about classes and other elements of the program.
Reflection is used in Serialization and also in JavaBeans, among other applications.
The class java.lang.Class is the starting point for the reflection process.
The classes in the package java.lang.reflect provide more information for reflection, and are typically used in collaboration with the class Class.
The Class Class
There exists a class in Java called — appropriately, but perhaps confusingly — Class. This class maintains run-time type information about the components of your program while it is running.
Class Class does not have a public constructor. Instead, you obtain an instance of this class in one of a number of ways. For example, by using:
- the getClass() method in class Object
String s; ... Class c = s.getClass();
- the forName() method in class Class
Class c = Class.forName("java.lang.String");
- the class operator
Class c1 = String.class; Class c2 = int.class; Class c3 = Double[].class;
Class has many useful methods you can use to obtain information about the element it describes.
For example, assume we have the following classes and interfaces:
package reflection;
public class Person
{
public Person()
{
}
public Person(String name,
int age)
{
m_name = name;
m_age = age;
}
public void init(String name,
int age)
{
m_name = name;
m_age = age;
}
public String getName()
{
return m_name;
}
public int getAge()
{
return m_age;
}
/////// Data ////////
private String m_name;
private int m_age;
}
package reflection;
public interface Firer
{
public void fire(Employee emp);
}
package reflection;
public interface Hirer
{
public void hire(Employee emp);
}
package reflection;
public interface HirerFirer
extends Hirer, Firer
{
}
package reflection;
public class Employee
extends Person
{
public Employee()
{
}
public Employee(String name,
int age,
double salary,
long id)
{
super(name, age);
m_salary = salary;
m_id = id;
}
public void init(String name,
int age,
double salary,
long id)
{
super.init(name, age);
m_salary = salary;
m_id = id;
}
public double getSalary()
{
return m_salary;
}
public long getId()
{
return m_id;
}
/////// Data ///////
private double m_salary;
private long m_id;
}
package reflection;
public class Manager
extends Employee
implements Hirer, Firer
{
public Manager()
{
}
public Manager(String name,
int age,
double salary,
long id)
{
super(name, age, salary, id);
}
public void hire(Employee emp)
{
// Details...
}
public void fire(Employee emp)
{
// Details...
}
}
Examining a Class
Given these classes and interfaces, here is an example that uses Class to obtain information about them:
package reflection;
public class ClassTestClass
{
public static void main(String[] args)
{
Class c;
try
{
c = Class.forName("java.lang.String");
printInfo(c);
}
catch(ClassNotFoundException e)
{
System.err.println("Class java.lang.String not found!");
}
c = int.class;
printInfo(c);
c = Double[].class;
printInfo(c);
c = int[].class;
printInfo(c);
short[] shortArray = new short[] {5, 10, 15};
c = shortArray.getClass();
printInfo(c);
c = Person.class;
printInfo(c);
c = Employee.class;
printInfo(c);
c = Manager.class;
printInfo(c);
c = Hirer.class;
printInfo(c);
c = HirerFirer.class;
printInfo(c);
}
private static void printInfo(Class c)
{
System.out.println("toString(): " + c.toString());
if (c.isPrimitive())
printPrimitiveInfo(c);
else if (c.isArray())
printArrayInfo(c);
else
printClassInfo(c);
}
private static void printPrimitiveInfo(Class c)
{
String typeName = c.getName();
System.out.println("Type " + typeName);
}
private static void printArrayInfo(Class c)
{
Class component = c.getComponentType();
System.out.println("Array of " + component.getName());
}
private static void printClassInfo(Class c)
{
String className = c.getName();
int index = className.lastIndexOf('.');
String packageName;
if (index != -1)
{
packageName = className.substring(0, index);
System.out.println("package " + packageName + ";");
className = className.substring(index+1, className.length());
}
String type = (c.isInterface())?"interface":"class";
System.out.println(type + " " + className);
Class superClass = c.getSuperclass();
if (superClass != null)
System.out.println(" extends " + superClass.getName());
Class[] interfaces = c.getInterfaces();
if ((interfaces != null) && (interfaces.length > 0))
{
if (c.isInterface())
System.out.print(" extends ");
else
System.out.print(" implements ");
for (int i = 0; i < interfaces.length; i++)
{
if (i > 0)
System.out.print(", ");
System.out.print(interfaces[i].getName());
}
System.out.println();
}
System.out.println("{");
System.out.println("}");
}
}
This outputs the following (I’ve bolded the toString(): line of each element output to make it easier to identify them visually):
toString(): class java.lang.String
package java.lang;
class String
extends java.lang.Object
implements java.io.Serializable
{
}
toString(): int
Type int
toString(): class [Ljava.lang.Double;
Array of java.lang.Double
toString(): class [I
Array of int
toString(): class [S
Array of short
toString(): class reflection.Person
package reflection;
class Person
extends java.lang.Object
{
}
toString(): class reflection.Employee
package reflection;
class Employee
extends reflection.Person
{
}
toString(): class reflection.Manager
package reflection;
class Manager
extends reflection.Employee
implements reflection.Hirer, reflection.Firer
{
}
toString(): interface reflection.Hirer
package reflection;
interface Hirer
{
}
toString(): interface reflection.HirerFirer
package reflection;
interface HirerFirer
extends reflection.Hirer, reflection.Firer
{
}
Array Name Encodings
Note the strange output for arrays. In the case of an array, the toString() method (and the getName() method) for Class returns a name like:
[Ljava.lang.Double;
Where the [ means “array of” and the remaining character(s) are chosen from the following table (see the Java Language Specification), with the specified meaning.
| B | byte |
| C | char |
| D | double |
| F | float |
| I | int |
| J | long |
| L | class or interface |
| S | short |
| Z | boolean |
For example, the following piece of code:
(new int[3][4][5][6][7][8][9]).getClass().getName()
returns:
[[[[[[[I
This strange encoding is used in a number of places in Java, notably in Reflection and also in JNI (Java Native Interface) when interfacing with code written in other languages (notably C and C++).
Creating a Class Instance
You can use the Class class to create a new instance of a specified class, by calling its newInstance() method:
package reflection;
public class NewInstanceTest
{
public static void main(String[] args)
{
try
{
Class empClass = Employee.class;
Employee emp = (Employee) empClass.newInstance();
emp.init("Eugene McCracken", 34, 45000, 45678);
System.out.println("Name: " + emp.getName());
Class mgrClass = Class.forName("reflection.Manager");
Manager mgr = (Manager) mgrClass.newInstance();
mgr.init("Pietro Botticelli", 385, 50000000, 675978);
System.out.println("Name: " + mgr.getName());
}
catch(ClassNotFoundException e)
{
System.err.println("Class not found");
}
catch(InstantiationException e)
{
System.err.println("Failed to instantiate");
}
catch(IllegalAccessException e)
{
System.err.println("Unable to access class");
}
}
}
which produces the following output:
Name: Eugene McCracken Name: Pietro Botticelli
The first created instance used a mechanism that depends on the class already having been loaded into the JVM.
The second created instance used the forName() method, which allows you to explicitly load a class (using its fully qualified name) before instantiating it. The only requirement here is that the class must exist in the class path.
The Reflection Classes
The package java.lang.reflect contains a number of classes to supplement the information that Class can provide:
- Constructor — describes a constructor for a class
- Field — describes a data field for a class
- Method — describes a method for a class
- Modifier — describes what modifiers (static, final, abstract, etc.) associated with an element.
In addition, java.lang.reflect contains an interface:
- Member
All reflection classes that represent a class member (Constructor, Field, Method) implement this interface.
Reconstructing a Class
Here’s an example of how we can reconstruct a class using reflection. This program is a modification of the eariler program that used just the class Class.
First, for purposes of exposition, I’ve added another class which adds additional features to be reconstructed:
package reflection;
/* package private */ class Company
implements HirerFirer
{
public Company()
{
}
public Company(String name, int maxEmps)
{
m_name = name;
m_employees = new Employee[maxEmps];
}
public void init(String name, int maxEmps)
{
m_name = name;
m_employees = new Employee[maxEmps];
}
public void hire(Employee emp)
{
m_employees[m_lastIndex++] = emp;
// Will throw an exception if index out of bounds
m_empCount++;
}
public void fire(Employee emp)
{
// Search for emp, and if found remove reference
for (int i = 0; i < m_lastIndex; i++)
{
if (m_employees[i] == emp)
{
m_employees[i] = null;
if (i == m_lastIndex)
m_lastIndex--;
m_empCount--;
return;
}
}
}
public Employee[] getEmployees()
{
return m_employees;
}
public void setEmployees(Employee[] emps)
{
m_employees = emps;
}
//// Data ////
private String m_name;
private Employee[] m_employees;
private int m_empCount = 0;
private int m_lastIndex = 0;
}
So here’s the example program that reconstructs a class using reflection:
package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class ClassTestClass2
{
public static void main(String[] args)
{
Class c;
try
{
c = Class.forName("java.lang.String");
printInfo(c);
}
catch(ClassNotFoundException e)
{
System.err.println("Class java.lang.String not found!");
}
c = int.class;
printInfo(c);
c = Double[].class;
printInfo(c);
c = int[].class;
printInfo(c);
short[] shortArray = new short[] {5, 10, 15};
c = shortArray.getClass();
printInfo(c);
c = Person.class;
printInfo(c);
c = Employee.class;
printInfo(c);
c = Manager.class;
printInfo(c);
c = Hirer.class;
printInfo(c);
c = HirerFirer.class;
printInfo(c);
c = Company.class;
printInfo(c);
c = ClassTestClass2.class;
printInfo(c);
}
private static void printInfo(Class c)
{
System.out.println("---- toString(): " +
c.toString() + " ----");
if (c.isPrimitive())
printPrimitiveInfo(c);
else if (c.isArray())
printArrayInfo(c);
else if (c.isInterface())
printInterfaceInfo(c);
else
printClassInfo(c);
}
private static void printPrimitiveInfo(Class c)
{
String typeName = c.getName();
System.out.println("Type " + typeName);
}
private static void printArrayInfo(Class c)
{
Class component = c.getComponentType();
System.out.println("Array of " + component.getName());
}
private static String[] getPackageAndName(String name)
{
int index = name.lastIndexOf('.');
String packageName = null;
if (index != -1)
{
packageName = name.substring(0, index);
name = name.substring(index+1, name.length());
}
return new String[] { packageName, name };
}
private static void printClassInfo(Class c)
{
String[] names = getPackageAndName(c.getName());
String packageName = names[0];
String className = names[1];
if (packageName != null)
System.out.println("package " + packageName + ";");
int mods = c.getModifiers();
System.out.println(Modifier.toString(mods) +
" class " + className);
Class superClass = c.getSuperclass();
if (superClass != null)
System.out.println(INDENT + INDENT +
"extends " + superClass.getName());
Class[] interfaces = c.getInterfaces();
if ((interfaces != null) && (interfaces.length > 0))
{
System.out.print(INDENT + INDENT + "implements ");
for (int i = 0; i < interfaces.length; i++)
{
if (i > 0)
System.out.print(", ");
System.out.print(interfaces[i].getName());
}
System.out.println();
}
System.out.println("{");
printMembers(c);
System.out.println("}");
}
private static void printInterfaceInfo(Class c)
{
String[] names = getPackageAndName(c.getName());
String packageName = names[0];
String className = names[1];
if (packageName != null)
System.out.println("package " + packageName + ";");
int mods = c.getModifiers();
System.out.println(Modifier.toString(mods) +
" interface " + className);
Class[] interfaces = c.getInterfaces();
if ((interfaces != null) && (interfaces.length > 0))
{
System.out.print(INDENT + INDENT + "extends ");
for (int i = 0; i < interfaces.length; i++)
{
if (i > 0)
System.out.print(", ");
System.out.print(interfaces[i].getName());
}
System.out.println();
}
System.out.println("{");
printMembers(c);
System.out.println("}");
}
private static void printMembers(Class c)
{
printConstructors(c);
printMethods(c);
printFields(c);
}
private static void printConstructors(Class c)
{
Constructor[] constructors = c.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++)
{
if (i == 0)
System.out.println(INDENT + "// Constructors");
Constructor con = constructors[i];
// Strip off package name from constructor name
String[] names = getPackageAndName(con.getName());
String conName = names[1];
int mods = con.getModifiers();
System.out.print(INDENT + Modifier.toString(mods));
System.out.print(" " + conName);
Class[] params = con.getParameterTypes();
printParams(params);
if (c.isInterface())
System.out.println(";");
else
{
System.out.println(" { " +
UNKNOWN_IMPLEMENTATION +
" }");
}
}
}
private static void printMethods(Class c)
{
Method[] methods = c.getDeclaredMethods();
for (int i = 0; i < methods.length; i++)
{
if (i == 0)
System.out.println(INDENT + "// Methods");
Method method = methods[i];
int mods = method.getModifiers();
System.out.print(INDENT + Modifier.toString(mods));
Class retType = method.getReturnType();
System.out.print(" " + getArrayType(retType) + " " +
method.getName() );
Class[] params = method.getParameterTypes();
printParams(params);
if (c.isInterface())
System.out.println(";");
else
System.out.println(" { " +
UNKNOWN_IMPLEMENTATION +
" }");
}
}
private static void printParams(Class[] params)
{
System.out.print("(");
for (int i = 0; i < params.length; i++)
{
if (i > 0)
System.out.print(", ");
System.out.print(getArrayType(params[i]) +
" p" + (i+1));
}
System.out.print(")");
}
private static void printFields(Class c)
{
Field[] fields = c.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
if (i == 0)
System.out.println(INDENT + "// Fields");
Field field = fields[i];
int mods = field.getModifiers();
System.out.print(INDENT + Modifier.toString(mods));
Class type = field.getType();
System.out.println(" " + getArrayType(type) +
" " + field.getName() + ";");
}
}
private static String getArrayType(Class c)
{
if (!c.isArray())
return c.getName();
else
{
// Use recursion to construct proper string
String type = getArrayType(c.getComponentType());
return type + "[]";
}
}
private static final String UNKNOWN_IMPLEMENTATION = "/* ??? */";
private static final String INDENT = " ";
}
The program isn’t quite complete, since it does not handle the following features:
- Full throws specifications for constructors and methods
- Nested and inner classes
Adding support for these features (and others?) is left as an exercise for the reader.
The above program produces the following output:
---- toString(): class java.lang.String ----
package java.lang;
public final synchronized class String
extends java.lang.Object
implements java.io.Serializable
{
// Constructors
public String() { /* ??? */ }
public String(java.lang.String p1) { /* ??? */ }
public String(char[] p1) { /* ??? */ }
public String(char[] p1, int p2, int p3) { /* ??? */ }
public String(byte[] p1, int p2, int p3, int p4) { /* ??? */ }
public String(byte[] p1, int p2) { /* ??? */ }
private String(byte[] p1, int p2, int p3, sun.io.ByteToCharConverter p4) { /* ??? */ }
public String(byte[] p1, int p2, int p3, java.lang.String p4) { /* ??? */ }
public String(byte[] p1, java.lang.String p2) { /* ??? */ }
public String(byte[] p1, int p2, int p3) { /* ??? */ }
public String(byte[] p1) { /* ??? */ }
public String(java.lang.StringBuffer p1) { /* ??? */ }
private String(int p1, int p2, char[] p3) { /* ??? */ }
// Methods
public int length() { /* ??? */ }
public char charAt(int p1) { /* ??? */ }
public void getChars(int p1, int p2, char[] p3, int p4) { /* ??? */ }
public void getBytes(int p1, int p2, byte[] p3, int p4) { /* ??? */ }
private byte[] getBytes(sun.io.CharToByteConverter p1) { /* ??? */ }
public byte[] getBytes(java.lang.String p1) { /* ??? */ }
public byte[] getBytes() { /* ??? */ }
public boolean equals(java.lang.Object p1) { /* ??? */ }
public boolean equalsIgnoreCase(java.lang.String p1) { /* ??? */ }
public int compareTo(java.lang.String p1) { /* ??? */ }
public boolean regionMatches(int p1, java.lang.String p2, int p3, int p4) { /* ??? */ }
public boolean regionMatches(boolean p1, int p2, java.lang.String p3, int p4, int p5) { /* ??? */ }
public boolean startsWith(java.lang.String p1, int p2) { /* ??? */ }
public boolean startsWith(java.lang.String p1) { /* ??? */ }
public boolean endsWith(java.lang.String p1) { /* ??? */ }
public int hashCode() { /* ??? */ }
public int indexOf(int p1) { /* ??? */ }
public int indexOf(int p1, int p2) { /* ??? */ }
public int lastIndexOf(int p1) { /* ??? */ }
public int lastIndexOf(int p1, int p2) { /* ??? */ }
public int indexOf(java.lang.String p1) { /* ??? */ }
public int indexOf(java.lang.String p1, int p2) { /* ??? */ }
public int lastIndexOf(java.lang.String p1) { /* ??? */ }
public int lastIndexOf(java.lang.String p1, int p2) { /* ??? */ }
public java.lang.String substring(int p1) { /* ??? */ }
public java.lang.String substring(int p1, int p2) { /* ??? */ }
public java.lang.String concat(java.lang.String p1) { /* ??? */ }
public java.lang.String replace(char p1, char p2) { /* ??? */ }
public java.lang.String toLowerCase(java.util.Locale p1) { /* ??? */ }
public java.lang.String toLowerCase() { /* ??? */ }
public java.lang.String toUpperCase(java.util.Locale p1) { /* ??? */ }
public java.lang.String toUpperCase() { /* ??? */ }
public java.lang.String trim() { /* ??? */ }
public java.lang.String toString() { /* ??? */ }
public char[] toCharArray() { /* ??? */ }
public static java.lang.String valueOf(java.lang.Object p1) { /* ??? */ }
public static java.lang.String valueOf(char[] p1) { /* ??? */ }
public static java.lang.String valueOf(char[] p1, int p2, int p3) { /* ??? */ }
public static java.lang.String copyValueOf(char[] p1, int p2, int p3) { /* ??? */ }
public static java.lang.String copyValueOf(char[] p1) { /* ??? */ }
public static java.lang.String valueOf(boolean p1) { /* ??? */ }
public static java.lang.String valueOf(char p1) { /* ??? */ }
public static java.lang.String valueOf(int p1) { /* ??? */ }
public static java.lang.String valueOf(long p1) { /* ??? */ }
public static java.lang.String valueOf(float p1) { /* ??? */ }
public static java.lang.String valueOf(double p1) { /* ??? */ }
public native java.lang.String intern() { /* ??? */ }
int utfLength() { /* ??? */ }
// Fields
private char[] value;
private int offset;
private int count;
private static final long serialVersionUID;
}
---- toString(): int ----
Type int
---- toString(): class [Ljava.lang.Double; ----
Array of java.lang.Double
---- toString(): class [I ----
Array of int
---- toString(): class [S ----
Array of short
---- toString(): class reflection.Person ----
package reflection;
public synchronized class Person
extends java.lang.Object
{
// Constructors
public Person() { /* ??? */ }
public Person(java.lang.String p1, int p2) { /* ??? */ }
// Methods
public int getAge() { /* ??? */ }
public java.lang.String getName() { /* ??? */ }
public void init(java.lang.String p1, int p2) { /* ??? */ }
// Fields
private java.lang.String m_name;
private int m_age;
}
---- toString(): class reflection.Employee ----
package reflection;
public synchronized class Employee
extends reflection.Person
{
// Constructors
public Employee() { /* ??? */ }
public Employee(java.lang.String p1, int p2, double p3, long p4) { /* ??? */ }
// Methods
public long getId() { /* ??? */ }
public double getSalary() { /* ??? */ }
public void init(java.lang.String p1, int p2, double p3, long p4) { /* ??? */ }
// Fields
private double m_salary;
private long m_id;
}
---- toString(): class reflection.Manager ----
package reflection;
public synchronized class Manager
extends reflection.Employee
implements reflection.Hirer, reflection.Firer
{
// Constructors
public Manager() { /* ??? */ }
public Manager(java.lang.String p1, int p2, double p3, long p4) { /* ??? */ }
// Methods
public void fire(reflection.Employee p1) { /* ??? */ }
public void hire(reflection.Employee p1) { /* ??? */ }
}
---- toString(): interface reflection.Hirer ----
package reflection;
public abstract interface interface Hirer
{
// Methods
public abstract void hire(reflection.Employee p1);
}
---- toString(): interface reflection.HirerFirer ----
package reflection;
public interface interface HirerFirer
extends reflection.Hirer, reflection.Firer
{
}
---- toString(): class reflection.Company ----
package reflection;
synchronized class Company
extends java.lang.Object
implements reflection.HirerFirer
{
// Constructors
public Company() { /* ??? */ }
public Company(java.lang.String p1, int p2) { /* ??? */ }
// Methods
public void fire(reflection.Employee p1) { /* ??? */ }
public reflection.Employee[] getEmployees() { /* ??? */ }
public void hire(reflection.Employee p1) { /* ??? */ }
public void init(java.lang.String p1, int p2) { /* ??? */ }
public void setEmployees(reflection.Employee[] p1) { /* ??? */ }
// Fields
private java.lang.String m_name;
private reflection.Employee[] m_employees;
private int m_empCount;
private int m_lastIndex;
}
---- toString(): class reflection.ClassTestClass2 ----
package reflection;
public synchronized class ClassTestClass2
extends java.lang.Object
{
// Constructors
public ClassTestClass2() { /* ??? */ }
// Methods
private static java.lang.String getArrayType(java.lang.Class p1) { /* ??? */ }
private static java.lang.String[] getPackageAndName(java.lang.String p1) { /* ??? */ }
public static void main(java.lang.String[] p1) { /* ??? */ }
private static void printArrayInfo(java.lang.Class p1) { /* ??? */ }
private static void printClassInfo(java.lang.Class p1) { /* ??? */ }
private static void printConstructors(java.lang.Class p1) { /* ??? */ }
private static void printFields(java.lang.Class p1) { /* ??? */ }
private static void printInfo(java.lang.Class p1) { /* ??? */ }
private static void printInterfaceInfo(java.lang.Class p1) { /* ??? */ }
private static void printMembers(java.lang.Class p1) { /* ??? */ }
private static void printMethods(java.lang.Class p1) { /* ??? */ }
private static void printParams(java.lang.Class[] p1) { /* ??? */ }
private static void printPrimitiveInfo(java.lang.Class p1) { /* ??? */ }
static java.lang.Class class$(java.lang.String p1) { /* ??? */ }
// Fields
private static final java.lang.String UNKNOWN_IMPLEMENTATION;
private static final java.lang.String INDENT;
static java.lang.Class array$Ljava$lang$Double;
static java.lang.Class array$I;
static java.lang.Class class$reflection$Person;
static java.lang.Class class$reflection$Employee;
static java.lang.Class class$reflection$Manager;
static java.lang.Class class$reflection$Hirer;
static java.lang.Class class$reflection$HirerFirer;
static java.lang.Class class$reflection$Company;
static java.lang.Class class$reflection$ClassTestClass2;
}
Note, in particular, the output for the class ClassTestClass2 itself is a little strange; it appears to have some extra static fields with strange names.
Inspecting a Class Instance
Horstmann and Cornell, in CORE Java, Volume I : Fundamentals, create a useful little class, ObjectAnalyzer, that allows you to peek into class instances at run time to determine their internal state. This could be useful for debugging, etc. ObjectAnalyzer is a class that has only static methods; it is non-instantiatable.
Here is a version modified to work under JDK 1.1 and beyond. It has also been enhanced to output information about arrays as well as classes:
package reflection;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
/**
* This class allows the programmer to peek into
* a specified class, and also to provide a generic
* equals() mechanism.
*
* @author Horstmann & Cornell, CORE Java 1.2, Vol I, p214
* @author Bryan J. Higgs, modified & extended 29 Dec 1999
*/
public class ObjectAnalyzer
{
private ObjectAnalyzer()
{
// Prevent instantiation
}
public static String toString(Object obj)
{
return toString(obj, 0);
}
private static String toString(Object obj,
int indentLevel)
{
String indent = "";
for (int i = 0; i < indentLevel; i++)
indent += " ";
Class c = obj.getClass();
if (c.isArray())
{
Class type = c.getComponentType();
String typeString = type.getName();
if (type.isArray())
typeString = getArrayType(type);
return indent + "Array of " + typeString + "\n" +
getArrayContents(obj, indentLevel);
}
String r = indent + c.getName();
Class sc = c.getSuperclass();
if (sc != null && !sc.equals(Object.class))
r += " extends " + sc;
r += "\n";
Field[] fields = c.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
Field field = fields[i];
r += field.getName() + "=";
String s = "???";// Expect access problems
try
{
Object value = field.get(obj);
Class vc = value.getClass();
if (!vc.isArray())
s = value.toString();
else
{
s = "\n";
s += getArrayContents(value,
indentLevel + 1);
}
}
catch(NullPointerException e)
{
s = "null";
}
catch(IllegalAccessException e)
{}
r += s;
if (!r.endsWith("\n"))
r += "\n";
}
return r;
}
private static String getArrayContents(Object obj,
int indentLevel)
{
String indent = "";
for (int i = 0; i < indentLevel; i++)
indent += " ";
Class c = obj.getClass();
String r = "";
int length = Array.getLength(obj);
for (int i = 0; i < length; i++)
{
r += indent + "[" + i + "]=";
String s = "???";// Expect access problems
try
{
Object value = Array.get(obj, i);
Class vc = value.getClass();
s = "(" + getArrayType(vc) + ")";
if (!vc.isArray())
{
s += value.toString();
}
else
{
s += "\n" +
getArrayContents(value,
indentLevel + 1);
}
}
catch(NullPointerException e)
{
s = "null";
}
r += s;
if (!r.endsWith("\n"))
r += "\n";
}
return r;
}
private static String getArrayType(Class c)
{
if (!c.isArray())
return c.getName();
else
{
// Use recursion to construct proper string
String type = getArrayType(c.getComponentType());
return type + "[]";
}
}
}
Here is a small test program to exercise it:
package reflection;
import java.util.Date;
import java.util.Vector;
public class ObjectAnalyzerTest
{
public static void main(String[] args)
{
String[] strings = new String[] {"Fred", "Joe", "Martha"};
System.out.println("strings: " +
ObjectAnalyzer.toString(strings));
System.out.println("System.out: " +
ObjectAnalyzer.toString(System.out));
Person[] parents = new Person[]
{
new Person("Vernon Clydesdale", 48), null
};
Person person =
new Person("Myrtle A. Fibbertigibbet", 24, parents);
System.out.println("Person: " +
ObjectAnalyzer.toString(person));
Object[] objects = new Object[]
{
"I am a String", new Person("Lionel Barksdale", 54),
new Object[]
{
"Another String", new Long(45L), new Vector(),
},
new Double(83.4), new Date()
};
System.out.println("objects: " +
ObjectAnalyzer.toString(objects));
}
//// Nested class for testing ////
static class Person
{
Person(String name, int age)
{
this(name, age, null);
}
Person(String name, int age, Person[] parents)
{
m_name = name;
m_age = age;
m_parents = parents;
m_count++;
}
public String toString()
{
return m_name;
}
///// Data ////
String m_name;
int m_age;
static int m_count = 0;
Person[] m_parents;
}
}
This outputs the following:
strings: Array of java.lang.String [0]=(java.lang.String)Fred [1]=(java.lang.String)Joe [2]=(java.lang.String)Martha System.out: java.io.PrintStream extends class java.io.FilterOutputStream autoFlush=??? trouble=??? textOut=??? charOut=??? closing=??? Person: reflection.ObjectAnalyzerTest$Person m_name=Myrtle A. Fibbertigibbet m_age=24 m_count=2 m_parents= [0]=(reflection.ObjectAnalyzerTest$Person)Vernon Clydesdale [1]=null objects: Array of java.lang.Object [0]=(java.lang.String)I am a String [1]=(reflection.ObjectAnalyzerTest$Person)Lionel Barksdale [2]=(java.lang.Object[]) [0]=(java.lang.String)Another String [1]=(java.lang.Long)45 [2]=(java.util.Vector)[] [3]=(java.lang.Double)83.4 [4]=(java.util.Date)Wed Dec 29 16:51:09 EST 1999
Note that the program cannot output the contents of the private fields in System.out, because Java’s access control won’t allow it. In JDK 1.2, however, they have added the ability to override the access control, subject to SecurityManager‘s approval.
Here’s a version of the program that adds this support (but note that it will only compile with JDK 1.2 or higher):
package reflection;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.AccessibleObject;
/**
* This class allows the programmer to peek into
* a specified class, and also to provide a generic
* equals() mechanism.
*
* @author Horstmann & Cornell, CORE Java 1.2, Vol I, p214
* @author Bryan J. Higgs, modified & extended 29 Dec 1999
*/
public class ObjectAnalyzer2
{
private ObjectAnalyzer2()
{
// Prevent instantiation
}
public static String toString(Object obj)
{
return toString(obj, 0);
}
private static String toString(Object obj,
int indentLevel)
{
String indent = "";
for (int i = 0; i < indentLevel; i++)
indent += " ";
Class c = obj.getClass();
if (c.isArray())
{
Class type = c.getComponentType();
String typeString = type.getName();
if (type.isArray())
typeString = getArrayType(type);
return indent + "Array of " + typeString + "\n" +
getArrayContents(obj, indentLevel);
}
String r = indent + c.getName();
Class sc = c.getSuperclass();
if (sc != null && !sc.equals(Object.class))
r += " extends " + sc;
r += "\n";
Field[] fields = c.getDeclaredFields();
try
{
AccessibleObject.setAccessible(fields, true);
}
catch(SecurityException e)
{
}
for (int i = 0; i < fields.length; i++)
{
Field field = fields[i];
r += field.getName() + "=";
String s = "???";// Expect access problems
try
{
Object value = field.get(obj);
Class vc = value.getClass();
if (!vc.isArray())
s = value.toString();
else
{
s = "\n";
s += getArrayContents(value,
indentLevel + 1);
}
}
catch(NullPointerException e)
{
s = "null";
}
catch(IllegalAccessException e)
{}
r += s;
if (!r.endsWith("\n"))
r += "\n";
}
return r;
}
private static String getArrayContents(Object obj,
int indentLevel)
{
String indent = "";
for (int i = 0; i < indentLevel; i++)
indent += " ";
Class c = obj.getClass();
String r = "";
int length = Array.getLength(obj);
for (int i = 0; i < length; i++)
{
r += indent + "[" + i + "]=";
String s = "???";// Expect access problems
try
{
Object value = Array.get(obj, i);
Class vc = value.getClass();
s = "(" + getArrayType(vc) + ")";
if (!vc.isArray())
{
s += value.toString();
}
else
{
s += "\n" +
getArrayContents(value,
indentLevel + 1);
}
}
catch(NullPointerException e)
{
s = "null";
}
r += s;
if (!r.endsWith("\n"))
r += "\n";
}
return r;
}
private static String getArrayType(Class c)
{
if (!c.isArray())
return c.getName();
else
{
// Use recursion to construct proper string
String type = getArrayType(c.getComponentType());
return type + "[]";
}
}
}
Instantiating an Object
Using the class Class, it is possible to dynamically instantiate a class at run time. The following snippet of code shows how this can be done, given just the fully qualified name of the class:
Class c;
// Load the class
try
{
c = Class.forName("reflection.Employee");
}
catch(ClassNotFoundException e)
{
System.err.println("Class not found");
return;
}
// Instantiate the class
Object emp;
try
{
emp = c.newInstance();
}
catch(InstantiationException e)
{
System.err.println("Failed to instantiate");
}
catch(IllegalAccessException e)
{
System.err.println("Unable to access class");
}
If the class file cannot be found in the class path, then a ClassNotFoundException is thrown.
If the specified object cannot be instantiated because it is an interface or is an abstract class, then an InstantiationException is thrown.
If the specified object cannot be instantiated because the caller does not have access to the object’s constructor, then an IllegalAccessException is thrown.
Note that the newInstance() method attempts to execute the zero-argument constructor for the class.
But what if a class doesn’t have such a constructor? Does this mean that it can’t be dynamically instantiated?
No, it doesn’t. However, it does mean that you can’t dynamically instantiate the class using only the class Class. Instead, you have to use the Constructor class’s newInstance() method:
public native Object newInstance(Object[] initargs)
throws InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
The initargs array supplies the values for the parameters to be passed to the constructor. The newInstance() method attempts to provide the necessary unwrapping conversions from objects (such as Double, Integer, etc.) to any expected primitive types (double, int, etc.)
If the number of actual and formal parameters differ, or if an unwrapping conversion fails, or if there is some other parameter mismatch, then an IllegalArgumentException is thrown.
If the constructor throws an exception, then the exception is wrapped in an InvocationTargetException, which is then thrown.
Here’s a snippet of code to show how you do it:
Class c;
// Load the class
try
{
c = Class.forName("reflection.Employee");
}
catch(ClassNotFoundException e)
{
System.err.println("Class not found");
return;
}
// Find a suitable constructor
Class[] paramTypes = new Class[]
{
String.class, int.class, double.class, long.class
};
Constructor con;
try
{
con = c.getConstructor(paramTypes);
}
catch(NoSuchMethodException e)
{
System.err.println("Can't find constructor");
return;
}
// Construct the parameters for the call
Object[] params = new Object[]
{
"Frodus G. Eklogue", new Integer(45),
new Double(34000), new Long(23456)
};
// Make the call to instantiate an Employee
Object emp = null;
try
{
emp = con.newInstance(params);
}
catch(InstantiationException e)
{
System.err.println("Failed to instantiate");
}
catch(IllegalAccessException e)
{
System.err.println("Unable to access class");
}
catch(InvocationTargetException e)
{
System.err.println("Invocation target threw exception");
}
Creating an Array
The Array class also has two newInstance() methods that allow you to dynamically create an array of a specified size and type:
- To create a single dimension array
public static Object newInstance(Class componentType,
int length)
throws NegativeArraySizeException
which does the equivalent of:
new componentType[length]
- To create a multiple dimension array
public static Object newInstance(Class componentType,
int[] dimensions)
throws IllegalArgumentException,
NegativeArraySizeException
which does the equivalent of
new componentType[dimensions[0]][dimensions[1]]...
Calling a Method
Just as the Constructor class provides a newInstance() method to invoke a chosen constructor for a class, so the Method class provides an invoke() method to call a chosen method in the class:
public native Object invoke(Object obj,
Object[] args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
Here’s a snippet of an example:
Class c = emp.getClass(); // (or similar)
...
// Find a suitable method
Class[] methodParams = new Class[]
{
String.class, int.class,
double.class, long.class
};
Method method;
try
{
method = c.getMethod("init", methodParams);
}
catch(NoSuchMethodException e)
{
System.err.println("Can't find method");
return;
}
// Construct parameter list for call
Object[] paramValues = new Object[]
{
"Launcelot Hobgoblin", new Integer(57),
new Double(560000.00), new Long(34567)
};
// Do the call...
try
{
method.invoke(emp, paramValues);
}
catch(IllegalAccessException e)
{
System.err.println("Unable to access class");
}
catch(InvocationTargetException e)
{
System.err.println("Invocation target threw exception");
}
An Example
Here’s an example of how you can use reflection to dynamically:
- Load a class
- Create an instance of that class
- Invoke a method of that class (a static method and an instance method)
First, we have a very simple class that is to be reflected:
package reflectee;
import java.util.Random;
/**
* A class that is to be reflected.
* When instantiated, it creates a random integer.
*/
public class ReflectMe
{
/**
* Creates a new instance of ReflectMe.
* (This constructor provided for simple instantiation.)
*/
public ReflectMe()
{
this(DEFAULT_MAX_RANDOM_VALUE);
}
/**
* Creates a new instance of ReflectMe
*
* @param maxRandomValue -- the random value will be
* between 0 and this value.
*/
public ReflectMe(int maxRandomValue)
{
m_randomValue = new Random().nextInt(maxRandomValue);
}
/**
* Gets the random value generated.
*/
public int getRandomValue()
{
return m_randomValue;
}
/**
* Sets the random value.
*/
public void setRandomValue(int value)
{
System.out.println("Setting value to " + value);
m_randomValue = value;
}
/**
* Main entry point
*/
public static void main(String[] args)
{
if (args.length < 1)
{
System.err.println("Usage: ReflectMe <maxRandomValue>");
}
else
{
// The following will throw a NumberFormatException
// if args[0] is not a value integer.
int maxRandomValue = Integer.parseInt(args[0]);
System.out.println("Generating random integer < " + maxRandomValue);
ReflectMe me = new ReflectMe(maxRandomValue);
System.out.println("Random value = " + me.getRandomValue());
}
}
//// Private data ////
private int m_randomValue; // The generated random integer
private static final int DEFAULT_MAX_RANDOM_VALUE = 100;
}
Now, here’s the class that will do the reflection:
package reflector;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* Class to use reflection on another class.
*/
public class MyReflector
{
/**
* Main entry point.
*
* @param args -- arg[0] : the fully qualified class
* name to reflect
* arg[1] : the name of the method to run
* arg[2] to arg[n] : arguments to pass to the method
*/
public static void main(String[] args)
{
if (args.length < 2)
{
System.err.println(
"Usage: MyReflector <fullClassName> <methodToRun> <argToPass>...");
}
else
{
// Extract the class name and the method
String className = args[0];
String methodName = args[1];
System.out.println("Class to reflect: " + className);
System.out.println("Method to call: " + methodName);
// Extract the arguments to pass and construct a new args array
String[] argsToPass = new String[args.length - 2];
for (int argIn = 2, argOut = 0; argIn < args.length; argIn++, argOut++)
{
argsToPass[argOut] = args[argIn];
}
System.out.println("Args to pass: ");
for (Object arg : argsToPass)
{
System.out.println(arg);
}
// Do the actual reflection work
try
{
reflectTheClass(className, methodName, argsToPass);
}
catch (ClassNotFoundException cnfe)
{
cnfe.printStackTrace();
}
catch (IllegalArgumentException iae)
{
iae.printStackTrace();
}
}
}
/**
* Does the actual reflection work
*/
public static void reflectTheClass(
String className, String methodName, String[] argsToPass)
throws ClassNotFoundException
{
// Now, load in the class to be reflected
Class theClass = Class.forName(className);
System.out.println("Class loaded; obtaining methods:");
Method[] methods = theClass.getMethods();
boolean methodFound = false;
for (Method method : methods)
{
String currentMethodName = method.getName();
System.out.println(currentMethodName);
if (currentMethodName.equals(methodName))
{
// We're making the simplified assumption that we have
// no overloaded methods of this name, so we just choose
// the first one we see.
methodFound = true;
System.out.println("Found the specified method");
// So call it...
callTheMethod(theClass, method, argsToPass);
// No need to search through any other methods.
return;
}
}
if (!methodFound)
{
throw new IllegalArgumentException(
"Method '" + methodName + "' not found!");
}
}
/**
* Calls the method from the specified class.
* (Makes the simplifying assumption that the method takes
* an array of arguments that matches what we have supplied.)
*/
private static void callTheMethod(
Class theClass, Method theMethod, String[] argsToPass)
{
int modifiers = theMethod.getModifiers();
try
{
if (Modifier.isStatic(modifiers))
{
// The method is static, so we can just call it
// (Note that we're assuming that this method has
// a signature that matches (String[] args) )
System.out.println("Calling static method '" + theMethod.getName() +
"' in class '" + theClass.getCanonicalName() + "' ...");
System.out.println(SEPARATOR);
// Note the first parameter (the instance to use) is ignored
// for a static method, so we just supply a null.
// We also assume that the signature for the method is
// (String[]) -- for example, a main method.
theMethod.invoke(null, new Object[] {argsToPass});
}
else
{
// The method is not static (it's an instance method),
// so we have to first create an instance of the class
// before we can call the method.
// First let's figure out what we have to supply
// for arguments.
System.out.println("Determining method parameter types.");
Class[] parameterTypes = theMethod.getParameterTypes();
// And based on the parameter types, construct an Object array
// to supply the matching values.
Object[] params = new Object[parameterTypes.length];
System.out.println("Total of " + parameterTypes.length + " Parameter types:");
for (int param= 0; param < params.length; param++)
{
Class paramType = parameterTypes[param];
System.out.println("[" + param + "] " + paramType);
// Extract the value from the argsToPass array, converting as appropriate:
// Simplification: Only handle int types
// Is it an int?
if (paramType == int.class)
{
params[param] = Integer.parseInt(argsToPass[param]); // autobox
}
else
{
// Do the appropriate conversion for other types
// (left as an exercise for the reader...)
}
}
// Now, create the instance of the class
System.out.println("Creating instance of class '" +
theClass.getCanonicalName() +
"' using default (no-arg) constructor...");
Object instance = theClass.newInstance();
// Finally, invoke the method on the instance
// passing in the parameters.
System.out.println("Calling instance method '" + theMethod.getName() +
"' in class '" + theClass.getCanonicalName() + "' ...");
System.out.println(SEPARATOR);
theMethod.invoke(instance, params);
}
System.out.println(SEPARATOR);
System.out.println("... Call completed.");
}
catch (IllegalArgumentException ex)
{
ex.printStackTrace();
}
catch (IllegalAccessException ex)
{
ex.printStackTrace();
}
catch (InvocationTargetException ex)
{
ex.printStackTrace();
}
catch (InstantiationException ex)
{
ex.printStackTrace();
}
}
//// Private data ////
private static final String SEPARATOR =
"-------------------------------------------------------------------";
}
Note that there is no reference to the ‘reflectee’ class from within the ‘reflector’ class. The idea is to make it possible for the ‘reflector’ class to be able to perform reflection on any class that might be within that JVM’s class path.
Invoking a static method
If we run the MyReflector class with the following parameters:
reflectee.ReflectMe main 50
the program outputs the following:
Class to reflect: reflectee.ReflectMe Method to call: main Args to pass: 50 Class loaded; obtaining methods: main Found the specified method Calling static method 'main' in class 'reflectee.ReflectMe' ... ------------------------------------------------------------------- Generating random integer < 50 Random value = 7 ------------------------------------------------------------------- ... Call completed.
In other words, it loaded in the reflectee.ReflectMe class, and then invoked that class’s main method, passing in a single argument (50).
Invoking an instance method
Here, we run the MyReflector class with the following parameters:
reflectee.ReflectMe setRandomValue 42
As a result, the program outputs the following:
Class to reflect: reflectee.ReflectMe Method to call: setRandomValue Args to pass: 42 Class loaded; obtaining methods: main getRandomValue setRandomValue Found the specified method Determining method parameter types. Total of 1 Parameter types: [0] int Creating instance of class 'reflectee.ReflectMe' using default (no-arg) constructor... Calling instance method 'setRandomValue' in class 'reflectee.ReflectMe' ... ------------------------------------------------------------------- Setting value to 42 ------------------------------------------------------------------- ... Call completed.
In other words, it loaded in the reflectee.ReflectMe class, created an instance thereof, and then invoked that class’s setRandomValue method, passing in a single argument of type int (42).
