Reflection
When developing JDK 1.1, the developers were confronted with a weakness in Java that made it impossible to develop certain types of tools and libraries: the static structure of classes and objects. In order to create an object, to call one of its methods or to access one of its member variables, the code of the class had to be known at compile time. While this is not a problem for most common applications, it is inadequate for developing generic tools and highly configurable applications that are extensible with plug-ins. In particular, the development of the bean and serialization APIs was not possible with the language properties available in version 1.0.
Rather, what was needed was the ability to load and instantiate classes (also with parameterized constructors) without their name having to be known at compile time. Furthermore, static or instance-based methods should be called and member variables should be accessible even if their name is only known when the program is running.
So a mechanism was sought that made these runtime system capabilities, which are normally requested by the compiler, available to "normal" applications. With the Reflection API of JDK 1.1, a library interface was created that implements all of the abilities mentioned (and a few more) and makes it available to any application as an integral part of the Java class library. Extensions to the language core were not necessary.
Java is an object-oriented language, normally you should create an object and you can access the fields (field) or call the method (method) of the object using the operator (.)
Java Reflection presents a different approach. You can access a field of the object if you know the name of the field. Or you can call a method of the object if you know the name of the method, the parameter structure of the method and the value to be assigned
Java Reflecion allows you to evaluate and change the structure and behavior of an object at runtime of the program. At the same time, it allows you to access the member private (private member) anywhere in the application. That doesn't work with the traditional approach.
- Java can usually be called Java Introspection. The program can evaluate the structure of an object at runtime.
- With Java Reflection, the program can evaluate the structure of an object at runtime and change the structure and behavior of the object
The classes Object and Class
The Object class is the parent class of all other classes and ensures that some elementary methods such as equals, toString, clone or hashCode are available in all classes.
With the getClass method of the Object class, any object can deliver a suitable class object. For each class that the runtime system uses, a class object of the Class type is created during the loading process. The class Class provides methods for querying properties of the class and allows classes to be loaded dynamically and instances to be generated dynamically. It is also the key to the functionality of the Reflection API.
Let us first take a look at the dynamic loading and instantiation of classes using a simple example.
public abstract class Animal {
public String getLocation() { return "Earth"; }
public abstract int getNumberOfLegs() ;
}
public interface Say {
public String say();
}
public class Cat extends Animal implements Say {
public static final String SAY = "meo meo";
public static final int NUMBER_OF_LEGS = 4;
private String name;
public int age;
public Cat() { }
public Cat(String name) { this.name = name; this.age = 1; }
public Cat(String name, int age) { this.name = name; this.age = age; }
public String getName() { return this.name; }
private void setName(String name) { this.name = name; }
public int getAge() { return this.age; }
public void setAge(int age) { this.age = age; }
@Override
public String say() { return SAY; }
@Override
public int getNumberOfLegs() { return NUMBER_OF_LEGS; }
}
Public methods
This is a simple example: Get the list from the public method of a class, including the inheritance method from the superclass and the interface.
public class ListMethod {
// Protected method
protected void info() { }
public static void testMethod1() { }
public void testMethod2() { }
public static void main(String[] args) {
// Get a list of public methods of this class
// Include methods inherited from the parent class, or interface.
Method[] methods = ListMethod.class.getMethods();
for (Method method : methods) {
System.out.println("Method " + method.getName());
}
}
}
// output:
Method main
Method testMethod1
Method testMethod2
Method wait
Method equals
Method toString
Method hashCode
Method getClass
Method notify
Method notifyAll
getMethods returns an array of objects of the type Method that contains an element for each public method of the class. The getDeclaredMethods method can also be used to list the non-public methods. The Method class provides some methods for accessing the method object. The most important are:
String getName ()
int getModifiers ()
Class [] getParameterTypes ()
Object invoke (Object obj, Object [] args)
With getName
the name of the method can be determined. getModifiers
provides a bit-linked representation of the method attributes (static, private, etc.). The return value can be passed to the static methods of the Modifier class in order to determine which attributes the method has:
static boolean isAbstract (int mod)
static boolean isExplicit (int mod)
static boolean isFinal (int mod)
static boolean isInterface (int mod)
static boolean isNative (int mod)
static boolean isPrivate (int mod)
static boolean isProtected (int mod)
static boolean isPublic (int mod)
static boolean isStatic (int mod)
static boolean isStrict (int mod)
static boolean isSynchronized (int mod)
static boolean isTransient (int mod)
static boolean isVolatile (int mod)
getParameterTypes
returns an array with objects of type Class which can be used to determine the number and types of formal arguments of the method. Each array element represents the class of the corresponding formal argument. If the array has the length 0, the method has no parameters. For example, if there are two elements with the types String and Double, the method has two parameters that are of the Type String and Double.
In order to be able to represent primitive types in this way, there is a static class object with the designation TYPE in the wrapper classes of the primitive types, which designates the associated primitive data type. An int argument is indicated, for example, by the fact that the return value of getParameterTypes contains an object of the type Integer.TYPE at the corresponding position. There are a total of nine such class objects, namely for the eight primitive types and for the "empty" return value void:
Class object | type |
---|---|
Boolean.TYPE | boolean |
Character.TYPE | char |
Byte.TYPE | byte |
Short.TYPE | short |
Integer.TYPE | int |
Long.TYPE | long |
Float.TYPE | float |
Double.TYPE | double |
Void.TYPE | void |
The invoke
method of the Method class is used to actually call the method represented by this method object. The first argument obj specifies the object on which the method is to be executed. Of course, it must belong to an object of the class on which getMethods
was called. The second argument passes the current parameters to the method. Similar to getParameterTypes
, an array is also specified here, the elements of which correspond to the corresponding current arguments. In the case of object parameters, an object of the appropriate type is simply placed at the desired location.
Class
Some important methods in Reflection relate to the class. Example of writing the basic information of the class such as the name of the class, package, modifier, etc.
public final class ShowClassInfo {
public static void main(String[] args) {
// Get Class object represent ShowClassInfo class.
Class<ShowClassInfo> aClass = ShowClassInfo.class;
// Print out class name, including the package name.
System.out.println("Class Name= " + aClass.getName());
// Print out simple class name (without package name).
System.out.println("Simple Class Name= " + aClass.getSimpleName());
Package pkg = aClass.getPackage(); // Package info
System.out.println("Package Name = " + pkg.getName());
int modifiers = aClass.getModifiers(); // Modifier
boolean isPublic = Modifier.isPublic(modifiers);
boolean isInterface = Modifier.isInterface(modifiers);
boolean isAbstract = Modifier.isAbstract(modifiers);
boolean isFinal = Modifier.isFinal(modifiers);
System.out.println("Is Public? " + isPublic); // true
System.out.println("Is Final? " + isFinal); // true
System.out.println("Is Interface? " + isInterface); // false
System.out.println("Is Abstract? " + isAbstract); // false
}
}
Constructor
The example of getting a constructor with the certain parameter. And write down this constructor's information.
public class Example {
public static void main(String[] args) throws NoSuchMethodException,... {
// Get Class object represent Cat class.
Class<Cat> aClass = Cat.class;
// Get the Constructor object of the public constructor
// that matches the specified parameterTypes
Constructor<?> constructor = aClass.getConstructor(String.class,int.class);
// Get parameter array of this constructor.
Class<?>[] paramClasses = constructor.getParameterTypes();
for (Class<?> paramClass : paramClasses) {
System.out.println("Param: " + paramClass.getSimpleName());
}
// Initialize the object in the usual way
Cat tom = new Cat("Tom", 3);
System.out.println("Cat 1: " + tom.getName() + ", age =" + tom.getAge());
// Using Java reflection to create object
Cat tom2 = (Cat) constructor.newInstance("Tom", 2);
System.out.println("Cat 2: " + tom.getName() + ", age =" + tom2.getAge());
}
}
Field
The example of getting the field with a specific name.
public class FieldExample {
public static void main(String[] args) throws NoSuchFieldException,... {
// Get Class object represent Cat class
Class<Cat> aClass = Cat.class;
// Get Field object represent field 'NUMBER_OF_LEGS'.
Field field = aClass.getField("NUMBER_OF_LEGS");
Class<?> fieldType = field.getType();
System.out.println("Field type: " + fieldType.getSimpleName());
Field ageField = aClass.getField("age");
Cat tom = new Cat("Tom", 5);
// Returns the value of the field represented by this Field,
// on the specified object.
Integer age = (Integer) ageField.get(tom);
System.out.println("Age = " + age);
// Sets the field represented by this Field object on
// the specified object argument to the specified new value.
ageField.set(tom, 7);
System.out.println("New Age = "+ tom.getAge());
}
}
Methods
The example of getting a Method by a name and the previously determined parameter. Writing down the information about the method, list of parameters.
public class MethodExample {
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
// Class object represent Cat class
Class<Cat> aClass = Cat.class;
// Method object represent getAge() method.
Method getAgeMethod = aClass.getMethod("getAge");
// return type of method.
Class<?> returnType= getAgeMethod.getReturnType();
System.out.println("Return type of getAge: "+ returnType.getSimpleName());
Cat tom = new Cat("Tom", 7);
// Call method 'getAge' way Reflect
// This is equivalent to calling: tom.getAge()
int age = (int) getAgeMethod.invoke(tom);
System.out.println("Age = " + age);
// Method object represent setAge(int) method of Cat class.
Method setAgeMethod = aClass.getMethod("setAge", int.class);
// Call method setAge(int) way Reflect
// This is equivalent to calling: tom.setAge(5)
setAgeMethod.invoke(tom, 5);
System.out.println("New Age = " + tom.getAge());
}
}
getter and setter method
The example below: the Public setter method and the Public getter method from the class listen.
public class GetSetExample {
// Method is getter if names start with get, and no parameters.
public static boolean isGetter(Method method) {
if (!method.getName().startsWith("get")) { return false; }
if (method.getParameterTypes().length != 0) { return false; }
if (void.class.equals(method.getReturnType())) { return false; }
return true;
}
// Method is setter if names start with set, and only one parameter.
public static boolean isSetter(Method method) {
if (!method.getName().startsWith("set")) { return false; }
if (method.getParameterTypes().length != 1) { return false; }
return true;
}
public static void main(String[] args) {
// Class object represet Cat class
Class<Cat> aClass = Cat.class;
// public methods
Method[] methods = aClass.getMethods();
for (Method method : methods) {
boolean isSetter = isSetter(method);
boolean isGetter = isGetter(method);
System.out.println("Method: " + method.getName());
System.out.println(" - Is Setter? " + isSetter);
System.out.println(" - Is Getter? " + isGetter);
}
}
}
Private fields
You cannot access the private method or field. The compiler of Java doesn't allow either. But it is possible with Java Reflection.
public class AccessPrivateFieldExample {
public static void main(String[] args) throws IllegalArgumentException,... {
// Class object represent Cat class
Class<Cat> aClass = Cat.class;
// Class.getField(String) get public field only.
// Use Class.getDeclaredField(String):
// Get the Field object of field declared in class.
Field private_nameField = aClass.getDeclaredField("name");
// Allows for access to private field.
// Avoid IllegalAccessException
private_nameField.setAccessible(true);
Cat tom = new Cat("Tom");
String fieldValue = (String) private_nameField.get(tom);
System.out.println("Value field name = " + fieldValue);
// Set new valud for 'name' field.
private_nameField.set(tom, "Tom Cat");
System.out.println("New name = " + tom.getName());
}
}
and the next example is the access to the private method.
public class AccessPrivateMethodExample {
public static void main(String[] args) throws NoSuchMethodException,... {
// Class object represent Cat class.
Class<Cat> aClass = Cat.class;
// Class.getMethod(String) get public method only.
// Use Class.getDeclaredMethod(String):
// Get the Method object of method declared in class.
Method private_setNameMethod = aClass.getDeclaredMethod("setName", String.class);
// Allows for access to private method.
// Avoid IllegalAccessException
private_setNameMethod.setAccessible(true);
Cat tom = new Cat("Tom");
// Call private method
private_setNameMethod.invoke(tom, "Tom Cat");
System.out.println("New name = " + tom.getName());
}
}
- Exercise 1: implement a program that examines some given Java class, and report about these:
- Method headers
- Return value data types
- Parameter data types