Reflection
Reflection is a feature in the Java programming language. It allows an executing Java program to examine or introspect upon itself and manipulate the internal properties of the program.
Note: The internal source of Spring Framework obtains class name information from the Spring configuration file and uses the reflection API to create beans.
The reflection classes, such as Method, are found in java.lang.reflect. To use these classes, You first need to obtain a java.lang.Class object for the class that you want to manipulate. You can get a Class object in two ways as follows:
Class<?> c = Class.forName("java.lang.String");
Class<?> c = String.class
java.lang.Class is used to represent classes and interfaces in a running Java program.
- 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 Account { public static final String SWIFT_CODE = "SCBLKRSE"; private int accountNo; private String owner; private int balance; public String message; public Account() { message = "Created at " + new Date(); } public Account(int accountNo, String owner) { this(); this.accountNo = accountNo; this.owner = owner; } public Account(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: The identification code of the financial institution. accountNo: The account number. owner: The name of the account holder. message: The message produced by an account.
Simulating the instanceof Operator
Once Class information is in hand, the next step is to ask fundamental questions about the Class object. For example, You can use the Class.isInstance method 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.Account"); boolean chk = c.isInstance(Integer.valueOf(10)); System.out.println(chk); chk = c.isInstance(new Account()); System.out.println(chk); } }
false true
Finding Out About Methods of a Class
One of the most practical and primary uses of reflection is finding out what methods are defined within 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.Account"); 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.Account return type = int ------------------------------------------ name = setBalance decl class = class net.java_school.examples.Account param #0 int return type = void ------------------------------------------ name = getBalance decl class = class net.java_school.examples.Account return type = int ------------------------------------------ name = setAccountNo decl class = class net.java_school.examples.Account param #0 int return type = void ------------------------------------------ name = toString decl class = class net.java_school.examples.Account return type = class java.lang.String ------------------------------------------ name = getOwner decl class = class net.java_school.examples.Account return type = class java.lang.String ------------------------------------------ name = setOwner decl class = class net.java_school.examples.Account param #0 class java.lang.String return type = void ------------------------------------------ name = deposit decl class = class net.java_school.examples.Account param #0 int return type = int ------------------------------------------ name = withdraw decl class = class net.java_school.examples.Account 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.
Obtaining Information About Constructors
You can use a similar approach to find out about the constructors of a class.
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.Account"); 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.Account decl class = class net.java_school.examples.Account param #0 int param #1 class java.lang.String param #2 int exception #0 class java.lang.RuntimeException -------------------------------------- name = net.java_school.examples.Account decl class = class net.java_school.examples.Account param #0 int param #1 class java.lang.String -------------------------------------- name = net.java_school.examples.Account decl class = class net.java_school.examples.Account --------------------------------------
Finding Out About Class Fields
It's also possible to find out which data fields are defined in a class.
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.Account"); 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.Account type = class java.lang.String modifiers = public static final ------------------------------------- name = accountNo decl class = class net.java_school.examples.Account type = int modifiers = private ------------------------------------- name = owner decl class = class net.java_school.examples.Account type = class java.lang.String modifiers = private ------------------------------------- name = balance decl class = class net.java_school.examples.Account type = int modifiers = private ------------------------------------- name = message decl class = class net.java_school.examples.Account type = class java.lang.String modifiers = public -------------------------------------
- getFields
- If you use getFields in the program, you can also obtain information about fields defined in super classes.
This example is similar to the previous ones. One new feature is the use of Modifier, which 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").
Invoking Methods by Name
So far, the examples that have been presented all relate to obtaining class information. But it's also possible to use reflection in other ways, for example, to invoke a method of a specified 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.Account"); Class<?>[] paramTypes = new Class<?>[1]; paramTypes[0] = Integer.TYPE; Method method = c.getMethod("deposit", paramTypes); Account account = new Account(1111, "John Doe", 100); Object[] arguments = new Object[1]; arguments[0] = Integer.valueOf(100); Object retobj = method.invoke(account, arguments); Integer retval = (Integer) retobj; System.out.println(retval.intValue()); } }
200
- How to obtain Class information on fundamental types
-
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;
Creating New Objects
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.Account"); Class<?>[] paramTypes = new Class[3]; paramTypes[0] = Integer.TYPE; paramTypes[1] = String.class; paramTypes[2] = Integer.TYPE; Constructor<?> constructor = c.getConstructor(paramTypes);//find constructor by parameter array Object[] arguments = new Object[3]; arguments[0] = Integer.valueOf(2222);//Account No arguments[1] = "Jane Doe";//Owner arguments[2] = Integer.valueOf(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. The following example illustrates this:
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.Account"); Account account = new Account(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! getField only get public field. field = c.getDeclaredField("balance"); field.setAccessible(true); field.setInt(account, 1000000); System.out.println(account); } }
Account No:3333|Owner:Alice|Balance:500|Message:Created at Thu Jan 30 18:30:13 KST 2020 Account No:3333|Owner:Alice|Balance:500|Message:Message changed with Reflection API Account No:3333|Owner:Alice|Balance:1000000|Message:Message changed with Reflection API
Final source: https://github.com/kimjonghoon/reflection
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.Account param #0 class java.lang.String return type = void ------------------------------------------ name = withdraw decl class = class net.java_school.examples.Account param #0 int exception #0 class java.lang.RuntimeException return type = int ------------------------------------------ name = getAccountNo decl class = class net.java_school.examples.Account return type = int ------------------------------------------ name = setAccountNo decl class = class net.java_school.examples.Account param #0 int return type = void ------------------------------------------ name = getBalance decl class = class net.java_school.examples.Account return type = int ------------------------------------------ name = setBalance decl class = class net.java_school.examples.Account param #0 int return type = void ------------------------------------------ name = deposit decl class = class net.java_school.examples.Account param #0 int return type = int ------------------------------------------ name = toString decl class = class net.java_school.examples.Account return type = class java.lang.String ------------------------------------------ name = getOwner decl class = class net.java_school.examples.Account return type = class java.lang.String ------------------------------------------ 3. name = net.java_school.examples.Account decl class = class net.java_school.examples.Account param #0 int param #1 class java.lang.String -------------------------------------- name = net.java_school.examples.Account decl class = class net.java_school.examples.Account -------------------------------------- name = net.java_school.examples.Account decl class = class net.java_school.examples.Account 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.Account type = class java.lang.String modifiers = public static final ------------------------------------- name = accountNo decl class = class net.java_school.examples.Account type = int modifiers = private ------------------------------------- name = owner decl class = class net.java_school.examples.Account type = class java.lang.String modifiers = private ------------------------------------- name = balance decl class = class net.java_school.examples.Account type = int modifiers = private ------------------------------------- name = message decl class = class net.java_school.examples.Account type = class java.lang.String modifiers = public ------------------------------------- 5. 200 6. Account No:2222|Owner:Jane Doe|Balance:1000|Message:Created at Thu Jan 30 18:44:28 KST 2020 7. Account No:3333|Owner:Alice|Balance:500|Message:Created at Thu Jan 30 18:44:28 KST 2020 Account No:3333|Owner:Alice|Balance:500|Message:Message changed with Reflection API Account No:3333|Owner:Alice|Balance:1000000|Message:Message changed with Reflection APIReferences