Last Modified 2019.3.4

Reflection

Reflection is a feature in the Java programming language. It allows an executing Java program to examine on itself, and manipulate internal properties of the program. There is no way in a Pascal, C, or C++ program to obtain information about the functions defined within that program.

Reflection is an API that allows a running program to retrieve class information loaded into a static area. If you know the class name, you can dynamically find information about the class through the reflection. The reflection API is in the java.lang.reflect package.

Reflection is used for the internal sources of Spring and RMI. The Spring Framework obtains the class name information from the Spring configuration file and uses the Java Reflection API to create the bean. Java RMI uses reflection to create connections with remote service objects.

To use reflection, you need to obtain a java.lang.Class object for the class that you want to manipulate. java.lang.Class is used to represent classes and interfaces in a running Java program. You can get a Class object in two ways as follows:

Class<?> c = Class.forName("java.lang.String");
Class<?> c = String.class
Class<T>
T - the type of the class modeled by this Class object. For example, the type of String.class is Class<String>. Use Class<?> if the class being modeled is unknown.

Create a bank account class as follows:

package net.java_school.examples;

import java.util.Date;

public class BankAccount {
	public static final String SWIFT_CODE = "SCBLKRSE";
	private int accountNo;
	private String owner;
	private int balance;
	public String message;

	public BankAccount() {
		message = "Created at " + new Date();
	}

	public BankAccount(int accountNo, String owner) {
		this();
		this.accountNo = accountNo;
		this.owner = owner;
	}

	public BankAccount(int accountNo, String owner, int balance) throws RuntimeException {
		this(accountNo, owner);
		if (balance < 0) throw new RuntimeException("Negative balance not allowed");
		this.balance = balance;
	}

	public int deposit(int amount) {
		balance += amount;
		return balance;
	}

	public int withdraw(int amount) throws RuntimeException {
		if (balance - amount < 0) throw new RuntimeException("Nagative balance not allowed");
		balance -= amount;
		return balance;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("Account No:");
		sb.append(this.getAccountNo());
		sb.append("|Owner:");
		sb.append(this.getOwner());
		sb.append("|Balance:");
		sb.append(this.getBalance());
		sb.append("|Message:");
		sb.append(this.message);

		return sb.toString();
	}

	public int getAccountNo() {
		return accountNo;
	}

	public void setAccountNo(int accountNo) {
		this.accountNo = accountNo;
	}

	public String getOwner() {
		return owner;
	}

	public void setOwner(String owner) {
		this.owner = owner;
	}

	public int getBalance() {
		return balance;
	}

	public void setBalance(int balance) {
		this.balance = balance;
	}

}

SWIFT_CODE is the identification code of the financial institution. accountNo is the account number. owner is the name of the account holder. message is the message produced by an account.

Simulating the instanceof Operator

Class.isInstance method can be used to simulate the instanceof operator.

package net.java_school.examples;

public class SimulatingTheInstanceofOperator {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName("net.java_school.examples.BankAccount");
		boolean chk = c.isInstance(Integer.valueOf(10));
		System.out.println(chk);
		chk = c.isInstance(new BankAccount());
		System.out.println(chk);
	}

}
false
true

Finding Out About Methods of a Class

package net.java_school.examples;

import java.lang.reflect.Method;

public class FindingMethods {

	public static void main(String[] args) throws Exception {
		Class <?> c = Class.forName("net.java_school.examples.BankAccount");
		Method[] ms = c.getDeclaredMethods();
		for (Method method : ms) {
			System.out.println("name = " + method.getName());
			System.out.println("decl class = " + method.getDeclaringClass());
			Class<?>[] ptypes = method.getParameterTypes();
			for (int i = 0; i < ptypes.length; i++) {
				System.out.println("param #" + i + " " + ptypes[i]);
			}
			Class<?>[] etypes = method.getExceptionTypes();
			for (int i = 0; i < etypes.length; i++) {
				System.out.println("exception #" + i + " " + etypes[i]);
			}
			System.out.println("return type = " + method.getReturnType());
			System.out.println("------------------------------------------");
		}
	}
}
name = getAccountNo
decl class = class net.java_school.examples.BankAccount
return type = int
------------------------------------------
name = setBalance
decl class = class net.java_school.examples.BankAccount
param #0 int
return type = void
------------------------------------------
name = getBalance
decl class = class net.java_school.examples.BankAccount
return type = int
------------------------------------------
name = setAccountNo
decl class = class net.java_school.examples.BankAccount
param #0 int
return type = void
------------------------------------------
name = toString
decl class = class net.java_school.examples.BankAccount
return type = class java.lang.String
------------------------------------------
name = getOwner
decl class = class net.java_school.examples.BankAccount
return type = class java.lang.String
------------------------------------------
name = setOwner
decl class = class net.java_school.examples.BankAccount
param #0 class java.lang.String
return type = void
------------------------------------------
name = deposit
decl class = class net.java_school.examples.BankAccount
param #0 int
return type = int
------------------------------------------
name = withdraw
decl class = class net.java_school.examples.BankAccount
param #0 int
exception #0 class java.lang.RuntimeException
return type = int
------------------------------------------
getMethods
If you use getMethods in the program instead of getDeclaredMethods, you can also obtain information for inherited methods.

Once a list of the Method objects has been obtained, it's simply a matter of displaying the information on parameter types, exception types, and the return type for each method. Each of these types, whether they are fundamental or class types, is in turn represented by a Class descriptor.

Obtaining Information About Constructors

package net.java_school.examples;

import java.lang.reflect.Constructor;

public class ObtainingInformationAboutConstructors {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName("net.java_school.examples.BankAccount");
		Constructor<?>[] constructors = c.getDeclaredConstructors();
		for (int i = 0; i < constructors.length; i++) {
			Constructor<?> constructor = constructors[i];
			System.out.println("name = " + constructor.getName());
			System.out.println("decl class = " + constructor.getDeclaringClass());
			Class<?>[] params = constructor.getParameterTypes();
			for (int j = 0; j < params.length; j++) {
				System.out.println("param #" + j + " " + params[j]);
			}
			Class<?>[] exceptions = constructor.getExceptionTypes();
			for (int j = 0; j < exceptions.length; j++) {
				System.out.println("exception #" + j + " " + exceptions[j]);
			}
			System.out.println("--------------------------------------");
		}
	}
}
name = net.java_school.examples.BankAccount
decl class = class net.java_school.examples.BankAccount
param #0 int
param #1 class java.lang.String
param #2 int
exception #0 class java.lang.RuntimeException
--------------------------------------
name = net.java_school.examples.BankAccount
decl class = class net.java_school.examples.BankAccount
param #0 int
param #1 class java.lang.String
--------------------------------------
name = net.java_school.examples.BankAccount
decl class = class net.java_school.examples.BankAccount
--------------------------------------

Finding Out About Class Fields

package net.java_school.examples;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class FindingFields {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName("net.java_school.examples.BankAccount");
		Field[] fields = c.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			System.out.println("name = " + field.getName());
			System.out.println("decl class = " + field.getDeclaringClass());
			System.out.println("type = " + field.getType());
			int mod = field.getModifiers();
			System.out.println("modifiers = " + Modifier.toString(mod));
			System.out.println("-------------------------------------");
		}
	}
}
name = SWIFT_CODE
decl class = class net.java_school.examples.BankAccount
type = class java.lang.String
modifiers = public static final
-------------------------------------
name = accountNo
decl class = class net.java_school.examples.BankAccount
type = int
modifiers = private
-------------------------------------
name = owner
decl class = class net.java_school.examples.BankAccount
type = class java.lang.String
modifiers = private
-------------------------------------
name = balance
decl class = class net.java_school.examples.BankAccount
type = int
modifiers = private
-------------------------------------
name = message
decl class = class net.java_school.examples.BankAccount
type = class java.lang.String
modifiers = public
-------------------------------------

One new feature is the use of Modifier. This is a reflection class that represents the modifiers found on a field member, for example "private int". The modifiers themselves are represented by an integer, and Modifier.toString is used to return a string representation in the "official" declaration order (such as "static" before "final").

getFields
If you use getFields in the program, you can also obtain information about fields defined in super classes.

Invoking Methods by Name

package net.java_school.examples;

import java.lang.reflect.Method;

public class InvokingMethodsByName {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName("net.java_school.examples.BankAccount");
		Class<?>[] paramTypes = new Class<?>[1];
		paramTypes[0] = Integer.TYPE;
		Method method = c.getMethod("deposit", paramTypes);
		BankAccount account = new BankAccount(1111, "John Doe", 100);
		Object[] arguments = new Object[1];
		arguments[0] = new Integer(100);
		Object retobj = method.invoke(account, arguments);
		Integer retval = (Integer) retobj;
		System.out.println(retval.intValue());
	}
}
200

You need to obtain Class information on fundamental types. The following approach accesses the predefined TYPE field of the wrapper (such as Integer) for the fundamental type.

Class<?> c = Integer.TYPE;

Obtaining Information About Constructors

package net.java_school.examples;

import java.lang.reflect.Constructor;

public class CreatingNewObjects {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName("net.java_school.examples.BankAccount");
		Class<?>[] paramTypes = new Class[3];
		paramTypes[0] = Integer.TYPE;
		paramTypes[1] = String.class;
		paramTypes[2] = Integer.TYPE;
		Constructor<?> constructor = c.getConstructor(paramTypes);//Find constructor with parameter array
		Object[] arguments = new Object[3];
		arguments[0] = new Integer(2222);//Account No
		arguments[1] = "Jane Doe";//Owner
		arguments[2] = new Integer(1000);//initial Balance
		Object retobj = constructor.newInstance(arguments);
		System.out.println(retobj);
	}
}
Account No:2222|Owner:Jane Doe|Balance:1000|Message:Created at Mon Jan 14 21:16:16 KST 2019

Changing Values of Fields

Another use of reflection is to change the values of data fields in objects. The value of this is again derived from the dynamic nature of reflection, where a field can be looked up by name in an executing program and then have its value changed. This is illustrated by the following example:

package net.java_school.examples;

import java.lang.reflect.Field;

public class ChangingValuesOfFields {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName("net.java_school.examples.BankAccount");
		BankAccount account = new BankAccount(3333, "Alice", 500);
		Field field = c.getField("message");
		System.out.println(account);
		field.set(account, "Message changed with Reflection API");
		System.out.println(account);
		field = c.getField("balance");//Runtime error. cannot access private field.
		field.setDouble(account, 1000000);
	}
}
Account No:3333|Owner:Alice|Balance:500|Message:Created at Mon Jan 14 21:16:56 KST 2019
Account No:3333|Owner:Alice|Balance:500|Message:Message changed with Reflection API
Exception in thread "main" java.lang.NoSuchFieldException: balance
	at java.lang.Class.getField(Class.java:1703)
	at net.java_school.examples.ChangingValuesOfFields.main(ChangingValuesOfFields.java:14)

The Eclipse project with source code for the examples is in the following zip file.
reflection.zip

How to run

~/reflection$ cd src/net/java_school/examples/
~/reflection/src/net/java_school/examples$ javac -d ../../../../bin *.java
~/reflection/src/net/java_school/examples$ cd -
~/reflection$ java -cp ./bin net.java_school.examples.Test
1.
false
true

2.
name = setOwner
decl class = class net.java_school.examples.BankAccount
param #0 class java.lang.String
return type = void
------------------------------------------
name = withdraw
decl class = class net.java_school.examples.BankAccount
param #0 int
exception #0 class java.lang.RuntimeException
return type = int
------------------------------------------
name = getAccountNo
decl class = class net.java_school.examples.BankAccount
return type = int
------------------------------------------
name = setAccountNo
decl class = class net.java_school.examples.BankAccount
param #0 int
return type = void
------------------------------------------
name = getBalance
decl class = class net.java_school.examples.BankAccount
return type = int
------------------------------------------
name = setBalance
decl class = class net.java_school.examples.BankAccount
param #0 int
return type = void
------------------------------------------
name = deposit
decl class = class net.java_school.examples.BankAccount
param #0 int
return type = int
------------------------------------------
name = toString
decl class = class net.java_school.examples.BankAccount
return type = class java.lang.String
------------------------------------------
name = getOwner
decl class = class net.java_school.examples.BankAccount
return type = class java.lang.String
------------------------------------------

3.
name = net.java_school.examples.BankAccount
decl class = class net.java_school.examples.BankAccount
param #0 int
param #1 class java.lang.String
--------------------------------------
name = net.java_school.examples.BankAccount
decl class = class net.java_school.examples.BankAccount
--------------------------------------
name = net.java_school.examples.BankAccount
decl class = class net.java_school.examples.BankAccount
param #0 int
param #1 class java.lang.String
param #2 int
exception #0 class java.lang.RuntimeException
--------------------------------------

4.
name = SWIFT_CODE
decl class = class net.java_school.examples.BankAccount
type = class java.lang.String
modifiers = public static final
-------------------------------------
name = accountNo
decl class = class net.java_school.examples.BankAccount
type = int
modifiers = private
-------------------------------------
name = owner
decl class = class net.java_school.examples.BankAccount
type = class java.lang.String
modifiers = private
-------------------------------------
name = balance
decl class = class net.java_school.examples.BankAccount
type = int
modifiers = private
-------------------------------------
name = message
decl class = class net.java_school.examples.BankAccount
type = class java.lang.String
modifiers = public
-------------------------------------

5.
200

6.
Account No:2222|Owner:Jane Doe|Balance:1000|Message:Created at Mon Mar 04 19:49:58 KST 2019

7.
Account No:3333|Owner:Alice|Balance:500|Message:Created at Mon Mar 04 19:49:58 KST 2019
Account No:3333|Owner:Alice|Balance:500|Message:Message changed with Reflection API
Exception in thread "main" java.lang.NoSuchFieldException: balance
	at java.base/java.lang.Class.getField(Class.java:1958)
	at net.java_school.examples.Test.main(Test.java:108)
References