Chapters
Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.
The ability to examine and manipulate a Java class from within itself may not sound like very much, but in other programming languages this feature simply doesn't exist. For example, there is no way in a Pascal, C, or C++ program to obtain information about the functions defined within that program.
One tangible use of reflection is in JavaBeans, where software components can be manipulated visually via a builder tool. The tool uses reflection to obtain the properties of Java components (classes) as they are dynamically loaded.
Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it. In this tutorial, I'm gonna demonstrate some essential methods of java.lang.Class and some essential classes of
The entry point for all reflection operations is java.lang.Class. With the exception of java.lang.reflect.ReflectPermission, none of the classes in java.lang.reflect have public constructors. Most Class objects refer to .class files of classes.
If there's an available instance and it's related to Object, we can use
Another way of getting Class object is using
In the example above,
To get declaring class, we use
A field is a class, interface, or enum with an associated value. Methods in the Field class can retrieve information about the field, such as its name, type, modifiers, and annotations.
This example demonstrates retrieving name, type, modifiers and annotations of a field.
Modifier class provides static methods and constants to decode class and member access modifiers. The sets of modifiers are represented as integers with distinct bit positions representing different modifiers. Bits that represent modifiers can be computed by using the OR(|) operator.
For example,
We can get and set field values by using Field's getter and setter methods. This example demonstrates getting and setting field values via reflection.
In the example above,
If we're getting a value from a non-static field, we need to instantiate the class where the field resides and then we can get the value of the field.
Use
If we're setting a value to a non-static field, we need to instantiate the class where the field resides and then we can set the value to the field.
If we wanna get/set private field value, we need to invoke
If you're having a problem accessing fields. You may try to check the accessibility of a field by invoking canAccess(Object obj) method. If it returns false, we need to set the accessiblity of the field to true by invoking setAccessible(boolean flag) method. Here's an example:
A method declaration includes the name, modifiers, parameters, return type, and list of throwable exceptions. The java.lang.reflect.Method class provides a way to obtain this information. This example demonstrates retrieving name, return type, modifiers, parameters and annotations of a method.
For example,
To get parameter names and other information regarding parameters, we can use the getParameters method. This method returns parameter information wrapped into a Parameter object. This example demonstrates getParameters method.
However, .class files do not store formal parameter names by default. This is because many tools that produce and consume class files may not expect the larger static and dynamic footprint of .class files that contain parameter names. In particular, these tools would have to handle larger .class files, and the Java Virtual Machine (JVM) would use more memory. In addition, some parameter names, such as secret or password, may expose information about security-sensitive methods.
To store formal parameter names in a particular .class file, and thus enable the Reflection API to retrieve formal parameter names, compile the source file with the -parameters option to the javac compiler.
If we compile our source code above with
The result is going to be:
The "arg0" and "arg1" that we see in the result above are synthesized parameter names of the form argN, where N is the index of the parameter in the descriptor of the method which declares the parameter.
Another way of getting method is invoking
If you wanna access a non-public method, use
We can invoke a method via reflection by using invoke method. This example demonstrates invoke method.
If you're having a problem accessing methods. You may try to set the accessibility of a method by invoking canAccess(Object obj) method. If it returns false, we need to set the accessiblity of the method to true by invoking setAccessible(boolean flag) method. Here's an example:
A constructor is used in the creation of an object that is an instance of a class. Typically it performs operations required to initialize the class before methods are invoked or fields are accessed. Constructors are never inherited.
Similar to methods, reflection provides APIs to discover and retrieve the constructors of a class and obtain declaration information such as the modifiers, parameters, annotations, and thrown exceptions.The java.lang.reflect.Constructor class provides a way to obtain this information.
This example demonstrates obtaining information regarding constructors such as modifiers, parameters, annotations and thrown exceptions.
Another way of getting class's constructor is to use
We can instantiate an object of a class via reflection by using
An array is an object of reference type which contains a fixed number of components of the same type; the length of an array is immutable. Creating an instance of an array requires knowledge of the length and component type. Each component may be a primitive type (such as byte, int, or double), a reference type (such as String or Object), or an array. Multi-dimensional arrays are really just arrays which contain components of array type.
Arrays are implemented in the Java virtual machine. The only methods on arrays are those inherited from Object. The length of an array is not part of its type; arrays have a length field which is accessible via
This example demonstrates creating a new array, getting an array and accessing array elements via reflection.
If you wanna obtain array type, use
If you're setting primitive value to a primitive-type array, use the setter methods in Array class that correspond to primitives like
Working on multi-dimensional arrays is similar to working on one-dimensional array. Note that multi-dimensional array is just an array inside an array. This example demonstrates creating and accessing multi-dimensional array.
If componentType represents an array class, the number of dimensions of the new array is equal to the sum of dimensions.length and the number of dimensions of componentType. In this case, the component type of the new array is the component type of componentType.
This example demonstrates the method.
An enum is a language construct that is used to define type-safe enumerations which can be used when a fixed set of named values is desired. All enums implicitly extend java.lang.Enum. Enums may contain one or more enum constants, which define unique instances of the enum type. An enum declaration defines an enum type which is very similar to a class in that it may have members such as fields, methods, and constructors (with some restrictions).
This example demonstrates accessing enums via reflection.
Take note that
Nest-Based feature is introduced in java 11. This feature links nested classes without creating bridge methods. Instead, this feature creates nest-based attributes in class files to determine the relationship between nested classes. Java adds access controls to java reflection in order to deal with the new feature.
a nest consists of a nest host and nest mates. Typically, nest host is the top-level class and nest mates are classes enclosed within a nest host. This example demonstrates methods that can access nest-based attributes.
Another interesting thing is that
That's why
If we change the "root" nest host to a subclass of Host class, the result will change. Try changing this line:
To this:
And the result will be:
In this chapter, I'm gonna discuss some exceptions that we may encounter when using java reflection and how to avoid them.
Getter and setter methods of Field class methods throw IllegalArgumentException if an argument can't be referenced to a designated field. This example demonstrates this scenario.
To this:
And IllegalArgumentException in the example above will be gone. Integer is an Object. Thus, we should use
Getter and setter methods of Field class support upcasting(widening) but don't support downcasting(narrowing). An exception will be thrown if we try to do downcasting. Take a look at this example.
IllegalAccessException may be thrown if an inaccessible private field is being accessed. In java versions prior to java 11, it is recommended to enable field accessibility by invoking
IllegalAccessException or IllegalArgumentException may be thrown if a final public or non-public field value is accessed. Take a look at this example.
NoSuchMethodException is thrown if we try to look for a method with Non-reifiable parameterized type. Take a look at this example.
Try changing this:
To this:
And NoSuchMethodException in the example above will be gone.
Reflection follows methods access restriction that is applied to direct invocation of methods. For example, private methods can only be accessed by its enclosing class and that enclosing class members. Take a look at this example.
Put it like this:
If it succeeds, java will print(without quotes) "Hello" on the console.
When we directly assign a null value to a varargs parameter, the varargs is "ambiguously zero-length". Thus, java doesn't consider a null value as varargs. Moreover, java still thinks something is wrong.
An InvocationTargetException wraps all exceptions (checked and unchecked) produced during the invocation of a method. Take a look at this example.
Any exception(checked or unchecked) that is thrown during object instantiation using
Sometimes, we may have problems regarding locating and invoking constructor. This example demonstrates some of the problems that we may encounter.
In
Try directly invoking the constructor like this:
and the result will be:
This example shows one of the differences between invoking constructor directly and invoking constructor via reflection.
In
Try directly invoking the constructor like this:
and the result will be:
This example shows another difference between invoking constructor directly against invoking constructor via reflection.
Reflection follows constructor access restriction that is applied to direct invocation of constructors. For example, private constructors can only be accessed by its enclosing class and that enclosing class members. Take a look at this example.
Put it like this:
Just like in Field class, Getter and setter methods of Array class methods throw IllegalArgumentException if an argument can't be placed in a particular array. This example demonstrates an attempt to set an int value to an index in an Integer-type array.
To this:
And IllegalArgumentException will be gone. Integer is an Object. Thus, we should use
Just like in Field class, Getter and setter methods of Array class support upcasting(widening) but don't support downcasting(narrowing). An exception will be thrown if we try to do downcasting. Take a look at this example.
Instantiation of enum types explicitly is forbidden in common coding and reflection. One of reasons why we can't is to preserve the uniqueness of enums. If we try to explicitly instantiate an enum type in reflection, IllegalArgumentException will be thrown. Take a look at this example.
Another problem that we may encounter is setting an incompatible enum constant to a enum variable. This will throw IllegalArgumentException. Take a look at this example.
Overview
Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.
The ability to examine and manipulate a Java class from within itself may not sound like very much, but in other programming languages this feature simply doesn't exist. For example, there is no way in a Pascal, C, or C++ program to obtain information about the functions defined within that program.
One tangible use of reflection is in JavaBeans, where software components can be manipulated visually via a builder tool. The tool uses reflection to obtain the properties of Java components (classes) as they are dynamically loaded.
Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it. In this tutorial, I'm gonna demonstrate some essential methods of java.lang.Class and some essential classes of
java.lang.reflect
package like Field, Method and Constructor.
Retrieving Class Object
The entry point for all reflection operations is java.lang.Class. With the exception of java.lang.reflect.ReflectPermission, none of the classes in java.lang.reflect have public constructors. Most Class objects refer to .class files of classes.
If there's an available instance and it's related to Object, we can use
getClass
method. This example demonstrates getClass method.
public class SampleClass{ public static void main(String[] args){ String str = "String"; Class<?> classObj = str.getClass(); System.out.println(classObj); } } Result class java.lang.StringIf there's no available instance, we can use the
.class
instance. This syntax requires name of type followed by ".class". This feature is also the easiest way of getting Class object for primitive types. This example demonstrates .class syntax.
public class SampleClass{ public static void main(String[] args){ Class<?> classObj = boolean.class; System.out.println(classObj); classObj = String.class; System.out.println(classObj); } } Result boolean class java.lang.Stringwhen
.class
syntax is used with primitive type. It returns a Class object that corresponds to the primitive type.
Another way of getting Class object is using
Class.forName
method. This static method verifies the fully-qualified class name in its argument. If the Class name is available, this method will return the corresponding Class object. This method has multiple forms. Also, this method can't be used with primitive type.
getName
method returns the Class name of a Class object. This example demonstrates Class.forName(String className)
method.
public class SampleClass{ public static void main(String[] args){ Class<?> classObj = Class.forName("java.lang.String"); System.out.println(classObj.getName()); } }For wrapper classes like Integer, Boolean and others; we can use the TYPE fields. Each wrapper class has TYPE field that returns a Class object of the specified wrapper class. This example demonstrates TYPE fields.
public class SampleClass{ public static void main(String[] args){ Class<?> classObj = Boolean.TYPE; System.out.println(classObj.getName()); } } Result booleanThere are other methods from the
Class
class that we can use to get nested Class objects. Although, these methods are not static. Thus, we need to have a Class object first. This example demonstrates getSuperClass
, getClasses
and getClasses
methods.
public class SampleClass{ public static void main(String[] args){ Class<?> classObj = javax.swing.JButton.class; System.out.println("Super Class"); System.out.println(classObj.getSuperclass().getName()); System.out.println(); Class<?>[] classes = Character.class.getClasses(); System.out.println("Classes"); for(Class<?> c : classes) System.out.println(c.getName()); System.out.println(); System.out.println("Declared Classes"); for(Class<?> c : Character.class.getDeclaredClasses()){ System.out.println(c.getName()); } } } Result Super Class class javax.swing.AbstractButton Classes class java.lang.Character$UnicodeBlock class java.lang.Character$UnicodeScript class java.lang.Character$Subset Declared Classes class java.lang.Character$CharacterCache class java.lang.Character$UnicodeBlock class java.lang.Character$UnicodeScript class java.lang.Character$Subset
getSuperclass
method returns superclass of the given class. In the example above, javax.swing.JButton
is the given class and getSuperclass
method returns javax.swing.AbstractButton
which is the superclass of JButton.
getClasses
method returns returns all the public classes, interfaces, and enums that are members of the class including inherited members. getDeclaredClasses
returns all public and non-public classes, interfaces, and enums.
In the example above,
CharacterCache
is a private class in Character class. It's included in the result of getDeclaredClasses method but not included in the result of getClasses method.
To get declaring class, we use
getDeclaringClass
method. This example demonstrate that method.
import java.lang.reflect.*; public class SampleClass{ public static void main(String[] args) throws NoSuchFieldException{ //Field class stores reflected information //regarding the specified field in a class Field f = System.class.getField("out"); Class<?> classObj = f.getDeclaringClass(); System.out.println(classObj.getName()); } } Result java.lang.System
out
is a standard output stream in java and it's declared in the System class in java.lang package. Anonymous class is not considered as "declared" in a class. Although, we can still get the class where an anonymous class resides by invoking getEnclosingClass
method. Take a look at this example.
public class SampleClass{ static Object o = new Object(){}; public static void main(String[] args){ Class<?> classObj = o.getClass().getDeclaringClass(); System.out.println(classObj); classObj = o.getClass().getEnclosingClass(); System.out.println(classObj); } } Result null class SampleClass
Accessing Fields
A field is a class, interface, or enum with an associated value. Methods in the Field class can retrieve information about the field, such as its name, type, modifiers, and annotations.
This example demonstrates retrieving name, type, modifiers and annotations of a field.
import java.lang.reflect.*; import java.lang.annotation.Annotation; public class SampleClass{ @Deprecated public static java.util.ArrayList<String> arlist; public static void main(String[] args) throws NoSuchFieldException{ //getField method returns a Field object where //information about the given field is stored. Field field = SampleClass.class.getField("arlist"); System.out.println("Name: " + field.getName()); System.out.println(); Class<?> type = field.getType(); System.out.println("Type: " + type.getName()); System.out.println(); Type genericType = field.getGenericType(); System.out.println("Generic Type: " + genericType); System.out.println(); int modifiers = field.getModifiers(); System.out.println("Modifiers: " + modifiers); System.out.println("Public field? " + Modifier.isPublic(modifiers)); System.out.println("Static field? " + Modifier.isStatic(modifiers)); System.out.println(); System.out.println("Annotations"); System.out.println(field.getAnnotation(Deprecated.class)); System.out.println( field.getAnnotation(SuppressWarnings.class)); } } Result Name: arList Type: java.util.ArrayList Generic Type: java.util.ArrayList<java.lang.String> Modifiers: 9 Public field? true Static field? true Annotations @java.lang.Deprecated(forRemoval=false, since="") null
getName
method in Field class returns the name of the field. getType
method in Field class returns a Class object that stores the type of the field. getGenericType
method returns a Type object that represents a generic type of the field if it's present in the Signature attribute
of the Class object.Otherwise, this method will rollback to getType
method. getModifiers
returns an integer value that represents one or more modifiers.
Modifier class provides static methods and constants to decode class and member access modifiers. The sets of modifiers are represented as integers with distinct bit positions representing different modifiers. Bits that represent modifiers can be computed by using the OR(|) operator.
For example,
Modifier.PUBLIC
is represented by integer value of 8. Modifier.STATIC
is represented by integer value of 1. 8|1 = 9
. getAnnotation
method returns an Annotation or one of its subclasses that holds the specified annotation if such annotation is applied to the field. Otherwise, the method returns null. Note that any annotation returned by this method is a declaration annotation. Also, getAnnotation
only returns annotation with RetentionPolicy.RUNTIME.
Getting and Setting Field Values
We can get and set field values by using Field's getter and setter methods. This example demonstrates getting and setting field values via reflection.
import java.lang.reflect.*; public class SampleClass{ public static int num = 5; public String str = "String"; public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException{ Field primitive = SampleClass.class.getField("num"); int value = primitive.getInt(primitive); System.out.println("Integer Original Value: " + value); System.out.println(); Field object = SampleClass.class.getField("str"); SampleClass sc = new SampleClass(); String s = (String)object.get(sc); System.out.println("String Original Value: " + s); System.out.println(); System.out.println("Setting values..."); System.out.println(); primitive.setInt(primitive, 10); value = primitive.getInt(primitive); System.out.println("Integer Current Value: " + value); System.out.println(); object.set(sc, new String("MyString")); s = (String)object.get(sc); System.out.println("String Current Value: " + s); System.out.println(); } } Result Integer Original Value: 5 String Original Value: String Setting Values... Integer Current Value: 10 String Current Value: MyStringUse
get(Object obj)
method when getting object-type fields. obj
parameter is the given field which value will be returned by the method. For primitive-type fields, It's preferrable to use get
methods that correspond to the type of the field.
In the example above,
primitive
variable is an int primitive. Thus, I used getInt
method. If we use the regular get
method with a primitive field, the return value will be wrapped into the corresponding wrapper class.
If we're getting a value from a non-static field, we need to instantiate the class where the field resides and then we can get the value of the field.
Use
set(Object obj, Object value)
method when setting object-type values. For primitive-type values, It's preferrable to use set
methods that correspond to the type of the field.
If we're setting a value to a non-static field, we need to instantiate the class where the field resides and then we can set the value to the field.
If we wanna get/set private field value, we need to invoke
getDeclaredField
method instead of getField
method. getDeclaredField
method returns public and non-public declared(but not inherited fields) fields in the Class object whereas getField
method only returns public fields(including inherited fields). This example demonstrates getting/setting private field value.
import java.lang.reflect.*; public class SampleClass{ private static int num = 5; public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException{ Field field = SampleClass.class.getDeclaredField("num"); System.out.println("Original Value: " + field.getInt(field)); field.setInt(field, 10); System.out.println("Current Value: " + field.getInt(field)); } }To get all public fields in a class, use
getFields
method. To get all public and non-public fields, use getDeclaredFields
method. Read their descriptions in the documentation. To know more about Field class, you can read this documentation.
If you're having a problem accessing fields. You may try to check the accessibility of a field by invoking canAccess(Object obj) method. If it returns false, we need to set the accessiblity of the field to true by invoking setAccessible(boolean flag) method. Here's an example:
SampleClass sc = new SampleClass();
Field field = sc.getClass().getField("field");
if(!field.canAccess(sc))
field.setAccessible(true);
System.out.println(field.getInt(sc));
Accessing Methods
A method declaration includes the name, modifiers, parameters, return type, and list of throwable exceptions. The java.lang.reflect.Method class provides a way to obtain this information. This example demonstrates retrieving name, return type, modifiers, parameters and annotations of a method.
import java.lang.reflect.*; import java.lang.annotation.Annotation; public class SampleClass{ @Deprecated public static <T extends String> String message(T msg1, T msg2) throws NullPointerException{ return msg1.concat(msg2); } public static void main(String[] args){ Method[] methods = SampleClass.class.getMethods(); for(Method m : methods){ if(!m.getName().equals("message")) continue; System.out.println("Name: " + m.getName()); System.out.println(); System.out.println("Annotations"); for(Annotation a : m.getDeclaredAnnotations()) System.out.println(a); System.out.println(); int modifiers = m.getModifiers(); System.out.println("Modifiers: "+modifiers); System.out.println("Is public? " + Modifier.isPublic(modifiers)); System.out.println("Is static? " + Modifier.isStatic(modifiers)); System.out.println(); System.out.println("Parameter Count: " + m.getParameterCount()); System.out.println("Parameter Types"); for(Class<?> param : m.getParameterTypes()) System.out.println(param.getTypeName()); System.out.println("Type Parameters"); for(TypeVariable tv : m.getTypeParameters()) System.out.println(tv.getName()); System.out.println(); System.out.println("Return Type: " + m.getReturnType().getName()); System.out.println(); System.out.println("Exceptions"); for(Class<?> exp : m.getExceptionTypes()) System.out.println(exp.getName()); } } } Result Name: message Annotations @java.lang.Deprecated(forRemoval=false, since="") Modifiers: 9 Is public? true Is static? true Parameter Count: 2 Parameter Types java.lang.String java.lang.String Type Parameters T Return Type: java.lang.String Exceptions java.lang.NullPointerException
getMethods
returns all methods including inherited methods. If you wanna only access declared method in a class, use getDeclaredMethods
method. getName
method in Method class returns the name of the method.
getDeclaredAnnotations
method returns annotations that are directly present on this element. This method ignores inherited annotations. If you wanna include inherited annotations, use getAnnotations method.
getModifiers
returns an integer value that represents one or more modifiers. Modifier class provides static methods and constants to decode class and member access modifiers. The sets of modifiers are represented as integers with distinct bit positions representing different modifiers. Bits that represent modifiers can be computed by using the OR(|) operator.
For example,
Modifier.PUBLIC
is represented by integer value of 8. Modifier.STATIC
is represented by integer value of 1. 8|1 = 9
. getParameterCount
method returns the total number of parameters in a method.
m.getParameterTypes
returns types of method parameters that are wrapped into Class objects. getTypeParameters
method returns type parameters in a method that are wrapped into TypeVariable interface.
getReturnType
method returns the return type of a method wrapped into a Class object. getExceptionTypes
returns exceptions thrown in a method.
To get parameter names and other information regarding parameters, we can use the getParameters method. This method returns parameter information wrapped into a Parameter object. This example demonstrates getParameters method.
import java.lang.reflect.*; public class SampleClass{ public void message(String str, int num){ System.out.println(str); System.out.println(num); } public static void main(String[] args){ for(Method m : SampleClass.class.getMethods()){ if(!m.getName().equals("message")) continue; System.out.println("Method Name: " + m.getName()); for(Parameter param : m.getParameters()){ System.out.println("Param Name: " + param.getName()); System.out.println("Param Type: " + param.getType().getName()); } } } } Result Method name: message Param Name: arg0 Param Type: java.lang.String Param Name: arg1 Param Type: intIn the result above, the names that we get are different from the parameter names in our source code. That's because .class files do not store formal parameter names by default. According to oracle:
However, .class files do not store formal parameter names by default. This is because many tools that produce and consume class files may not expect the larger static and dynamic footprint of .class files that contain parameter names. In particular, these tools would have to handle larger .class files, and the Java Virtual Machine (JVM) would use more memory. In addition, some parameter names, such as secret or password, may expose information about security-sensitive methods.
To store formal parameter names in a particular .class file, and thus enable the Reflection API to retrieve formal parameter names, compile the source file with the -parameters option to the javac compiler.
If we compile our source code above with
-parameters
flag like this:javac SampleClass.java -parameters
The result is going to be:
Method name: message
Param Name: str
Param Type: java.lang.String
Param Name: num
Param Type: int
The "arg0" and "arg1" that we see in the result above are synthesized parameter names of the form argN, where N is the index of the parameter in the descriptor of the method which declares the parameter.
Another way of getting method is invoking
getMethod
method. This example demonstrates getMethod.
import java.lang.reflect.*; public class SampleClass{ public void meth(String msg){ System.out.println(msg); } public static void main(String[] args) throws NoSuchMethodException{ Method m = SampleClass.class.getMethod("meth", String.class); System.out.println("Method Name: " + m.getName()); } } Result: Method Name: meth
getMethod(String name, Class<?>... parameterTypes)
has two parameters. name
parameter is the name of the method that we are looking for. parameterTypes
is used to determine the number and types of parameters of the method that we're looking for. getMethod
can obtain a public method(inherited or declared in the specified class).
If you wanna access a non-public method, use
getDeclaredMethod
method. Unlike getMethod
, getDeclaredMethod
can obtain a non-public method(declared in the specified class only).
Invoking Method
We can invoke a method via reflection by using invoke method. This example demonstrates invoke method.
import java.lang.reflect.*; public class SampleClass{ String invokeMe(String msg1, String msg2){ System.out.println("invokeMe Method!"); System.out.println("message1: " + msg1); System.out.println("message2: " + msg2); return msg1+msg2; } static void noParam(){ System.out.println("noParam Method!"); } public static void main(String[] args) throws IllegalAccessException, InvocationTargetException{ for(Method m : SampleClass.class.getDeclaredMethods()){ if(m.getName().equals("noParam")){ System.out.println("Invoke noParam"); Object o = m.invoke(m); System.out.println("return value"); System.out.println(o); System.out.println(); } else if(m.getName().equals("invokeMe")){ System.out.println("Invoke invokeMe"); Object o = m.invoke(new SampleClass(), "Hello","Everyone"); System.out.println("return value"); System.out.println(o); System.out.println(); } } } } Result Invoke noParam noParam Method! return value null Invoke invokeMe invokeMe Method! message1: Hello message2: Everyone return value HelloEveryone
invoke(Object obj, Object... args)
has two parameters. obj
parameter is the object where the method we're going to invoke resides. args
parameter is a varargs that accepts objects as method arguments. Number of arguments in args
parameter must match the number of arguments in the method.
If you're having a problem accessing methods. You may try to set the accessibility of a method by invoking canAccess(Object obj) method. If it returns false, we need to set the accessiblity of the method to true by invoking setAccessible(boolean flag) method. Here's an example:
SampleClass sc = new SampleClass(); Method[] m = sc.getClass().getDeclaredMethods()
if(m.length != 0){ if(!m[0].canAccess(sc)) m[0].setAccessible(true); m[0].invoke(sc); }
Accessing Constructors
A constructor is used in the creation of an object that is an instance of a class. Typically it performs operations required to initialize the class before methods are invoked or fields are accessed. Constructors are never inherited.
Similar to methods, reflection provides APIs to discover and retrieve the constructors of a class and obtain declaration information such as the modifiers, parameters, annotations, and thrown exceptions.The java.lang.reflect.Constructor class provides a way to obtain this information.
This example demonstrates obtaining information regarding constructors such as modifiers, parameters, annotations and thrown exceptions.
import java.lang.reflect.*; import java.lang.annotation.Annotation; public class SampleClass{ @Deprecated public SampleClass(String s1, Integer i1) throws IllegalStateException{ } public static void main(String[] args){ for(Constructor c : SampleClass.class.getConstructors()){ System.out.println("Name: " + c.getName()); System.out.println(); System.out.println("Parameters"); for(Parameter p : c.getParameters()){ System.out.println("Param Name: " + p.getName()); System.out.println("Param Type: " + p.getType().getName()); } System.out.println(); System.out.println("Modifiers"); System.out.println(c.getModifiers()); System.out.println(); System.out.println("Exceptions"); for(Class<?> ex : c.getExceptionTypes()) System.out.println(ex.getName()); System.out.println(); System.out.println("Annotations"); for(Annotation a : c.getDeclaredAnnotations()) System.out.println(a); } } } Result Name: SampleClass Parameters Param Name: arg0 Param Type: java.lang.String Param Name: arg1 Param Type: java.lang.Integer Modifiers 1 Exceptions java.lang.IllegalStateException Annotations @java.lang.Deprecated(forRemoval=false, since="")
getConstructors
returns all public constructors in a class. If you wanna obtain non-public constructors, use getDeclaredConstructors
method. getName
method in Constructor class returns the name of the constructor. Other methods that I used here like getParameters
, getExceptionTypes
, etc. are explained in the "Accessing Methods" and "Invoking Methods" topics. You should read those topic first before reading this topic.
Another way of getting class's constructor is to use
getConstructor
method. This example demonstrates getConstructor method.
import java.lang.reflect.*; public class SampleClass{ String str; public SampleClass(String str){ this.str = str; } public static void main(String[] args) throws NoSuchMethodException{ Constructor c = SampleClass.class.getConstructor(String.class); System.out.println("Parameters"); for(Parameter p : c.getParameters()){ System.out.println("Param Name: " + p.getName()); System.out.println("Param Type: " + p.getType().getName()); } } } Result Parameters Param Name: args0 Param Type: java.lang.String
getConstructor(Class<?>... parameterType)
has a varargs parameter that is used to identify specific constructor that we wanna access. In the example ebove, I put String.class
as argument. Meaning, I'm looking for a constructor in SampleClass with only a String stype parameter.
Creating New Instance
We can instantiate an object of a class via reflection by using
newInstance(Object... initargs)
method. This method invokes a constructor that has the same parameter signature as the parameters in the method. This example demonstrates newInstance method.
import java.lang.reflect.*; public class SampleClass{ private String str; private int num; SampleClass(String str, int num){ this.str = str; this.num = num; } void display(){ System.out.println(str + " | " + num); } public static void main(String[] args) throws InstantiationException, IllegalAccessException, InvocationTargetException{ Constructor[] cons = SampleClass.class.getDeclaredConstructors(); SampleClass sc = null; if(cons.length != 0){ sc = (SampleClass)cons[0].newInstance("Hello!",5); } else throw new NullPointerException(); sc.display(); } } Result Hello! | 5If you're having a problem accessing constructors. You may try to set the accessibility of a constructor by invoking setAccessible(boolean flag) method. Here's an example:
Constructor[] cons = SampleClass.class.getDeclaredConstructors()
if(cons.length != 0){ cons[0].setAccessible(true); SampleClass sc = cons[0].newInstance("Hello!", 5); sc.display(); }
Accessing Arrays
An array is an object of reference type which contains a fixed number of components of the same type; the length of an array is immutable. Creating an instance of an array requires knowledge of the length and component type. Each component may be a primitive type (such as byte, int, or double), a reference type (such as String or Object), or an array. Multi-dimensional arrays are really just arrays which contain components of array type.
Arrays are implemented in the Java virtual machine. The only methods on arrays are those inherited from Object. The length of an array is not part of its type; arrays have a length field which is accessible via
getLength()
in java.lang.reflect.Array class.
This example demonstrates creating a new array, getting an array and accessing array elements via reflection.
import java.lang.reflect.*; public class SampleClass{ static String[] strArr; public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException{ Field field = SampleClass.class.getDeclaredField("strArr"); Class<?> type = field.getType(); if(type.isArray()){ System.out.println("Array Name: " + field.getName()); System.out.println("Array Type: " + type.getComponentType()); //create new array Object o = Array.newInstance(type.getComponentType(), 3); //set elements for(int i = 0; i < 3; i++) Array.set(o, i, String.valueOf("a"+i)); //get elements System.out.println("Reflection"); System.out.println("length: " + Array.getLength(o)); System.out.println("Elements"); for(int i = 0; i < 3; i++) System.out.println(Array.get(o, i)); System.out.println(); //assign our newly created array to strArr field.set(field,o); System.out.println("strArr"); System.out.println("length: " + strArr.length); System.out.println("Elements"); for(String s : strArr) System.out.println(s); System.out.println(); //get array field String[] strArr2 = (String[])field.get(field); System.out.println("strArr2"); System.out.println("length: " + strArr2.length); System.out.println("Elements"); for(String s : strArr2) System.out.println(s); } } } Result Array Name: strArr Array Type: class java.lang.String Reflection length: 3 Elements a0 a1 a2 strArr length: 3 Elements a0 a1 a2 strArr2 length: 3 Elements a0 a1 a2
isArray()
verifies if a type is an array type. Returns true if the type is an array. Otherwise, returns false. getComponentType
(interchangeable with componentType
method) method returns a Class object with component type of an array. Otherwise, returns null. ComponentType of an array is the type of element that we can put in the array. In the example above, the component type of strArr
array is java.lang.String.
If you wanna obtain array type, use
getType()
method. This method returns array type in this format: braces+L+component-type. Example, [Ljava.lang.String represents a one-dimensional array with java.lang.String as its component type. [[Ljava.lang.String represents a two-dimensional array with java.lang.String as its component type.
newInstance(Class<?> componentType, int length)
in set(Object array, int index, Object value)
method in Array class creates a new array. componentType
is the component type of the array and length
is the number of elements in array.
set(Object array, int index, Object value)
method in Array class sets a value in a specified index. array
parameter is the array where we wanna set a value, index
parameter is where the value is going to be placed and value
is the value to be placed in the specified index in the specified array.
If you're setting primitive value to a primitive-type array, use the setter methods in Array class that correspond to primitives like
setInt
, setBoolean
and others.
getLength
in Array class returns the total length of an array. get(Object array, int index)
method retrives a value in an array. array
parameter is the array where the value is to be obtained. index
parameter is the location of the value to be obtained. If you're getting primitive value from a primitive-type array, use getter methods in Arrayclass that correspond to primitives like getInt
, getBoolean
and others.
Working on Multi-Dimensional Arrays
Working on multi-dimensional arrays is similar to working on one-dimensional array. Note that multi-dimensional array is just an array inside an array. This example demonstrates creating and accessing multi-dimensional array.
import java.lang.reflect.*; public class SampleClass{ public static void main(String[] args){ //2x1 array Object o = Array.newInstance(String[].class,2); Array.set(o,0,new String[]{"a","b"}); Array.set(o,1,new String[]{"c","d"}); Object row0 = Array.get(o,0); Object row1 = Array.get(o,1); System.out.println("Row1"); for(String s : (String[])row0) System.out.println(s); System.out.println(); System.out.println("Row2"); for(String s : (String[])row1) System.out.println(s); System.out.println(); } } Result Row1 a b Row2 c dAnother way of creating multi-dimensional array is by invoking the second form of
newInstance
method. newInstance(Class<?> componentType, int... dimensions)
Creates a new array with the specified component type and dimensions. If componentType represents a non-array class or interface, the new array has dimensions.length dimensions and componentType as its component type.
If componentType represents an array class, the number of dimensions of the new array is equal to the sum of dimensions.length and the number of dimensions of componentType. In this case, the component type of the new array is the component type of componentType.
This example demonstrates the method.
import java.lang.reflect.*; public class SampleClass{ public static void main(String[] args){ //2x2 array Object o = Array.newInstance(int.class,2,2); Object row1 = Array.get(o,0); Array.set(row1,0,2); Array.set(row1,1,4); Object row2 = Array.get(o,1); Array.set(row2,0,6); Array.set(row2,1,8); System.out.println("Row1"); for(int i : (int[])row1) System.out.println(i); System.out.println(); System.out.println("Row2"); for(int i : (int[])row2) System.out.println(i); System.out.println(); } } Result Row1 2 4 Row2 6 8Another example.
import java.lang.reflect.*; public class SampleClass{ public static String[] str = new String[1]; public static void main(String[] args) throws NoSuchFieldException{ Field field = SampleClass.class.getField("str"); //1x2 array Object o = Array.newInstance(field.getType(),1,2); System.out.println(Array.getLength(o)); Object o1 = Array.get(o,0); System.out.println(Array.getLength(o1)); Array.set(o1,0,new String[]{"a","b","c"}); Array.set(o1,1,new String[]{"d","e"}); for(String s : (String[])Array.get(o1,0)) System.out.println(s); for(String s : (String[])Array.get(o1,1)) System.out.println(s); } } Result 1 2 a b c d e
Accessing Enums
An enum is a language construct that is used to define type-safe enumerations which can be used when a fixed set of named values is desired. All enums implicitly extend java.lang.Enum. Enums may contain one or more enum constants, which define unique instances of the enum type. An enum declaration defines an enum type which is very similar to a class in that it may have members such as fields, methods, and constructors (with some restrictions).
This example demonstrates accessing enums via reflection.
import java.lang.reflect.*; enum Days{ MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY; final boolean field1 = false; final int field2 = 1; } public class SampleClass{ public static Days day = Days.WEDNESDAY; public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException{ Class<?> c = Class.forName("Days"); if(c.isEnum()){ System.out.println("enum name: " + c.getName()); int mod = c.getModifiers(); System.out.println("Modifiers: " + mod); System.out.println("Is public? " + Modifier.isPublic(mod)); System.out.println("Is static? " + Modifier.isStatic(mod)); System.out.println("Is final? " + Modifier.isFinal(mod)); System.out.println(); System.out.println("Constants"); for(Object o : c.getEnumConstants()) System.out.println(o); System.out.println(); System.out.println("Fields"); for(Field f : c.getDeclaredFields()) if(f.isEnumConstant()) System.out.println("Constant: " + f.getName()); else System.out.println("Field: " + f.getName()); System.out.println(); Class<?> sc = SampleClass.class; Field enumField = sc.getField("day"); if(enumField.getType().isEnum()){ System.out.println("Enum field value"); System.out.println(enumField.get(enumField)); System.out.println("Set value"); enumField.set(enumField, Days.SATURDAY); System.out.println("Enum field value"); System.out.println(enumField.get(enumField)); } } } } Result enum name: Days Modifiers: 16400 Is public? false Is static? false Is final? true Constants MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY Fields Constant: MONDAY Constant: TUESDAY Constant: WEDNESDAY Constant: THURSDAY Constant: FRIDAY Constant: SATURDAY Constant: SUNDAY Field: field1 Field: field2 Field: $VALUES Enum field value WEDNESDAY Set value Enum field value SATURDAY
isEnum
method returns true if a Class object is an enum. Otherwise, returns false. getEnumConstants
returns enum constants in the order they're declared in source code.
Take note that
getDeclaredFields
and getFields
methods don't guarantee the order of enum constants. use getEnumConstants
if enum constants are needed to be ordered.
isEnumConstant
in Field class returns true if the field is an enum constants. Otherwise, returns false. We can also get and set fields with enum values by using get
and set
methods of Field class.
Java Reflection's Nest-Based Access Control Feature
Nest-Based feature is introduced in java 11. This feature links nested classes without creating bridge methods. Instead, this feature creates nest-based attributes in class files to determine the relationship between nested classes. Java adds access controls to java reflection in order to deal with the new feature.
a nest consists of a nest host and nest mates. Typically, nest host is the top-level class and nest mates are classes enclosed within a nest host. This example demonstrates methods that can access nest-based attributes.
import java.util.ArrayList; public class SampleClass{ public static void main(String[] args){ Class<?> hostClass = Host.class; ArrayList<Class<?>> list = new ArrayList<>(); System.out.println (hostClass.getName() + " class nest mates/members"); for(Class<?> c : hostClass.getNestMembers()){ System.out.println(c.getName()); list.add(c); } System.out.println(); for(Class<?> c : list){ System.out.println("Nest host of " + c.getName()); System.out.println(c.getNestHost().getName()); System.out.println (c.getName()+" nest mate of " + hostClass.getName()); System.out.println(c.isNestmateOf(hostClass)); System.out.println(); } } } class Host{ class ClassA{ class ClassC{ } } class ClassB{ } } Host class nest mates/members Host Host$ClassB Host$ClassA Host$ClassA$ClassC Nest host of Host Host nest mate of Host true Nest host of Host$ClassB Host Host$ClassB nest mate of Host true Nest host of Host$ClassA Host Host$ClassA nest mate of Host true Nest host of Host$ClassA$ClassC Host Host$ClassA$ClassC nest mate of Host true
getNestMembers
returns an array of nest mates of a nest host. There are interesting things in the result above. First off, the "root" nest host(Host class) considers itself as its nest mate. This could mean that the nest host can access itself which is pretty normal.
Another interesting thing is that
ClassC
is a nest mate of Host class. You might ask why ClassA
is not the nest host of ClassC
. Note that inheritance is different from nest-based feature. Nest-based feature is about links or communication lines, if you will, between nested classes whereas inheritance is about relationships between classes.
That's why
ClassC
is not nest mate of ClassA
even ClassC
is a subclass of ClassA
. The nest host of ClassC
is Host
because, in my opinion, ClassC
requires to link to Host
first and then Host
links ClassC
to ClassA
.
getNestHost
returns the nest host of a Class object that invokes this method. isNestmateOf
method tests if the Class object that invokes this method is a nest mate of the given Class object in the argument. Returns true if the test is successful. Otherwise, returns false.
If we change the "root" nest host to a subclass of Host class, the result will change. Try changing this line:
Class<?> hostClass = Host.class;
To this:
Class<?> hostClass = ClassA.class;
And the result will be:
ClassA nest mates/members ClassA Nest host of ClassA ClassA nest mate of ClassA trueIn the example above, the nest host and nest mate of
ClassA
is itself. That's because ClassA
is the "root" nest host and "root" host considers itself as its nest host and nest mate. Although, ClassC
is still not a nest mate of ClassA
because in the class file of ClassC
, Host
class is the nest host of ClassC
.
Troubleshooting
In this chapter, I'm gonna discuss some exceptions that we may encounter when using java reflection and how to avoid them.
Fields
Getter and setter methods of Field class methods throw IllegalArgumentException if an argument can't be referenced to a designated field. This example demonstrates this scenario.
import java.lang.reflect.*; public class SampleClass{ public static Integer num; public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException{ Field f = SampleClass.class.getField("num"); f.setInt(f,4); } } Result IllegalArgumentException...To avoid this problem, use the appropriate set method for a specific type. Try changing this:
f.setInt(f,4);
To this:
f.set(f, Integer.valueOf(4));
And IllegalArgumentException in the example above will be gone. Integer is an Object. Thus, we should use
set
method.
Getter and setter methods of Field class support upcasting(widening) but don't support downcasting(narrowing). An exception will be thrown if we try to do downcasting. Take a look at this example.
import java.lang.reflect.*; public class SampleClass{ public static int num = 0; public static void main(String[] args) throws Exception{ Field f = SampleClass.class.getField("num"); //upcasting works f.setShort(f,(short)4); System.out.println(SampleClass.num); //downcasting doesn't work //IllegalArgumentException f.setLong(f,2L); } } Result 4 IllegalArgumentException...If we try to obtain field from a non-public Field object using
getField
method, java will throw NoSuchFieldException. Take a look at this example.
import java.lang.reflect.*; public class SampleClass{ private static Integer num; public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException{ Field f = SampleClass.class.getField("num"); f.set(f, Integer.valueOf(4)); } } Result NoSuchFieldException...Use
getDeclaredField
method when accessing non-public fields.
IllegalAccessException may be thrown if an inaccessible private field is being accessed. In java versions prior to java 11, it is recommended to enable field accessibility by invoking
setAccessible
method. This method accepts a boolean value as an argument. set the argument to true to enable accessibility. Otherwise, set it to false.
IllegalAccessException or IllegalArgumentException may be thrown if a final public or non-public field value is accessed. Take a look at this example.
import java.lang.reflect.*; public class SampleClass{ public final int num = 4; public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException{ Field f = SampleClass.class.getField("num"); f.setInt(f, 4); //This will throw an exception too //Object o = f.getInt(f); } } Result IllegalArgumentException...
Methods
NoSuchMethodException is thrown if we try to look for a method with Non-reifiable parameterized type. Take a look at this example.
import java.lang.reflect.*; public class SampleClass{ public static void main(String[] args) throws NoSuchMethodException{ Class<?> c = new ClassA<String>().getClass(); Method m = c.getMethod("meth",String.class); } } class ClassA<T>{ public void meth(T o){ System.out.println(o); } } Result NoSuchMethodException...Remember that non-reifiable type like
T
and the type that is going to replace T
which is String
will be erased due to type erasure. Instead, T
will be replaced with its upper bound which is Object type.
Try changing this:
Method m = c.getMethod("meth",String.class);
To this:
Method m = c.getMethod("meth",Object.class);
And NoSuchMethodException in the example above will be gone.
Reflection follows methods access restriction that is applied to direct invocation of methods. For example, private methods can only be accessed by its enclosing class and that enclosing class members. Take a look at this example.
import java.lang.reflect.*; public class SampleClass{ public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException{ ClassA<String> ca = new ClassA<>(); Class<?> c = ca.getClass(); Method m = c.getDeclaredMethod("meth",Object.class); m.invoke(ca,"Hello"); } } class ClassA<T>{ private void meth(T o){ System.out.println(o); } } Result IllegalAccessException...
meth
can't be invoked outside ClassA
because meth
is a private method. We can use setAccessible
method to suppress IllegalAccessException in this situation. Add this line of code before invoking the method:m.setAccessible(true)
Put it like this:
m.setAccessible(true);
m.invoke(ca,"Hello");
If it succeeds, java will print(without quotes) "Hello" on the console.
invoke
method has been retrofitted to be a variable-arity method(method that takes one or more arguments). Although, sometimes, this can cause ambiguity. Take a look at this example.
Source: The Java™ Tutorials import java.lang.reflect.Method; public class MethodTroubleToo { public void ping() { System.out.format("PONG!%n"); } public static void main(String... args) { try { MethodTroubleToo mtt = new MethodTroubleToo(); Method m = MethodTroubleToo.class.getMethod("ping"); switch(Integer.parseInt(args[0])) { // works case 0: m.invoke(mtt); break; // works (expect compiler warning) case 1: m.invoke(mtt, null); break; // IllegalArgumentException case 2: Object arg2 = null; m.invoke(mtt, arg2); break; // IllegalArgumentException case 3: Object arg4 = new Object[0]; m.invoke(mtt, arg4); break; // works case 4: m.invoke(mtt, new Object[0]); break; default: System.out.format("Test not found%n"); } // production code should //handle these exceptions more gracefully } catch (Exception x) { x.printStackTrace(); } } }In the example above,
case 0
works fine because it met the right criteria for invoking ping
method. case 1
works but it's kinda ambiguous because of the null
argument. This case works because we're assigning a null value to a varargs parameter.
When we directly assign a null value to a varargs parameter, the varargs is "ambiguously zero-length". Thus, java doesn't consider a null value as varargs. Moreover, java still thinks something is wrong.
case 2
doesn't work because null
is assigned to an Object-type variable. Once we put a variable in the argument-list of a method, this variable will be considered as a regular argument even the value of that variable is null. This explanation is also applicable to case 3
.
case 4
works because we're assigning a zero-length array to varargs. Zero-length array is equivalent to zero-length varargs.
An InvocationTargetException wraps all exceptions (checked and unchecked) produced during the invocation of a method. Take a look at this example.
import java.lang.reflect.*; public class SampleClass{ public float compute(int num){ System.out.println("Invoke compute method"); return num/0; } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException{ Class<?> c = SampleClass.class; Method m = c.getMethod("compute", int.class); Object f = m.invoke(new SampleClass(),5); System.out.println("result: " + f); } } Result Invoke compute method InvocationTargetException... ... Caused by: java.lang.ArithmeticException: / by zero ...If an InvocationTargetException is thrown, the method was invoked. Diagnosis of the problem would be the same as if the method was called directly and threw the exception. This exception does not indicate a problem with the reflection package or its usage.
Constructors
Any exception(checked or unchecked) that is thrown during object instantiation using
newInstance
method will be wrapped into an InvocationTargetException.
import java.lang.reflect.*; public class SampleClass{ int num = 0; public SampleClass(int num){ this.num = num/0; } public static void main(String[] args) throws InstantiationException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{ Constructor c = SampleClass.class.getConstructor(int.class); SampleClass sc = (SampleClass)c.newInstance(5); } } Result InvocationTargetException... ... Caused by: java.lang.ArithmeticException: /by zero ...If an InvocationTargetException is thrown, the constructor was invoked. Diagnosis of the problem would be the same as if the constructor was called directly and threw the exception. This exception does not indicate a problem with the reflection package or its usage.
Sometimes, we may have problems regarding locating and invoking constructor. This example demonstrates some of the problems that we may encounter.
public class SampleClass{ SampleClass(){ System.out.println ("Constructor with 0 parameters"); } SampleClass(Integer num){ System.out.println("Integer: " + num); } SampleClass(Object obj){ System.out.println("Object: " + obj); } SampleClass(String str){ System.out.println("String: " + str); } public static void main(String[] args) throws Exception{ Class<?> c = SampleClass.class; int num = 0; switch(num){ case 0: //IllegalArgumentException: wrong //number of arguments c.getDeclaredConstructor(). newInstance("Hello"); break; case 1: //NoSuchMethodException //no constructor with int parameter c.getDeclaredConstructor(int.class). newInstance(4); break; case 2: //Result: Object: Hi //Doesn't perform method/constructor //resolution c.getDeclaredConstructor(Object.class). newInstance("Hi"); break; default: System.err.println("Invalid number!"); break; } } }In
case 0
, We looked for a constructor without parameter. Then, we attempted to invoke a constructor with parameter. IllegalArgumentException is thrown because we tried to invoke a constructor that didn't correspond with the constructor that was returned by getConstructor
method.
In
case 1
, we attemped to look for a constructor with int
parameter. NoSuchMethodException is thrown because such constructor doesn't exist. Remember that int
primitive is different from Integer
class. If you expect java to autobox int value and invokes a constructor with Integer parameter, if won't happen because autoboxing doesn't work with reflection.
Try directly invoking the constructor like this:
new SampleClass(4);
and the result will be:
Integer: 4
This example shows one of the differences between invoking constructor directly and invoking constructor via reflection.
In
case 2
, constructor with Object parameter was invoked. Method/Constructor resolution doesn't work with reflection. Method/Constructor resolution is a mechanism that looks for a method/constructor that is mostly suitable for the given arguments. In the example above, the constructor with String parameter should have been invoked if the Method/Constructor resolution was working with reflection.
Try directly invoking the constructor like this:
new SampleClass("Hi");
and the result will be:
String: Hi
This example shows another difference between invoking constructor directly against invoking constructor via reflection.
Reflection follows constructor access restriction that is applied to direct invocation of constructors. For example, private constructors can only be accessed by its enclosing class and that enclosing class members. Take a look at this example.
public class SampleClass{ public static void main(String[] args) throws Exception{ ClassA.class.getDeclaredConstructor(). newInstance(); } } class ClassA{ private ClassA(){ System.out.println ("ClassA Private Constructor!"); } } Result IllegalAccessException...
Class
constructor can't be invoked outside ClassA
because the constructor is a private method. We can use setAccessible
method to suppress IllegalAccessException in this situation. Add this line of code before invoking the constructor:m.setAccessible(true)
Put it like this:
Constructor c = ClassA.class.getConstructor(); c.setAccessible(true); c.newInstance();If it succeeds, the message in the constructor will be printed.
Arrays
Just like in Field class, Getter and setter methods of Array class methods throw IllegalArgumentException if an argument can't be placed in a particular array. This example demonstrates an attempt to set an int value to an index in an Integer-type array.
import java.lang.reflect.Array; public class SampleClass{ public static void main(String[] args) throws Exception{ Integer[] arr = new Integer[2]; Array.setInt(arr,0,4); } } Result IllegalArgumentException...To solve this problem, use the appropriate method for a specific array-type. change this line of codes:
Array.setInt(arr,0,4);
To this:
Array.set(arr,0,Integer.valueOf(4));
And IllegalArgumentException will be gone. Integer is an Object. Thus, we should use
set
method.
Just like in Field class, Getter and setter methods of Array class support upcasting(widening) but don't support downcasting(narrowing). An exception will be thrown if we try to do downcasting. Take a look at this example.
import java.lang.reflect.Array; public class SampleClass{ public static void main(String[] args){ int[] nums = new int[1]; //upcasting works Array.setShort(nums,0,(short)2); System.out.println(nums[0]); //downcasting doesn't work //IllegalArgumentException Array.setLong(nums,0,4L); } } Result 2 IllegalArgumentException...In reflection, If we get/set a value of an zero-length array, ArrayIndexOutOfBoundsException will be thrown. Take a look at this example.
import java.lang.reflect.Array; public class SampleClass{ public static void main(String[] args){ int[] nums = new int[0]; int num = Array.getInt(nums,0); System.out.println(num); } } Result ArrayIndexOutOfBoundsException...In common code, this problem occurs in a few cases. In reflection, you may encounter this in several cases. If you encounter ArrayIndexOutOfBoundsException, you may check the length of the array you're accessing by invoking
getLength
method in order to know if you're dealing with zero-length array or not.
Enums
Instantiation of enum types explicitly is forbidden in common coding and reflection. One of reasons why we can't is to preserve the uniqueness of enums. If we try to explicitly instantiate an enum type in reflection, IllegalArgumentException will be thrown. Take a look at this example.
import java.lang.reflect.*; public class SampleClass{ public static void main(String[] args) throws Exception{ Constructor[] cons = Weekends.class.getDeclaredConstructors(); for(Constructor c : cons){ System.out.println("Name: " + c.getName()); c.setAccessible(true); c.newInstance(); } } } enum Weekends{ SATURDAY, SUNDAY; Weekends(){ System.out.println("Invoking..."); } } Result Name: Weekends ...IllegalArgumentException...To avoid this problem, check first if the Class object that you wanna instantiate is an enum type by invoking
isEnum
method.
Another problem that we may encounter is setting an incompatible enum constant to a enum variable. This will throw IllegalArgumentException. Take a look at this example.
import java.lang.reflect.Field; enum A1{ A,B } enum A2{ C,D } public class SampleClass{ private static A1 a1 = A1.A; public static void main(String[] args) throws Exception{ Field f = SampleClass.class.getDeclaredField("a1"); f.set(f,A2.C); } } Result IllegalArgumentException...To avoid this problem, we can check if an enum constant can be assigned to an enum variable by invoking
isAssignableFrom
method. This method returns true if the given object type can be assigned to the object type in Field object. Otherwise, returns false. Let's modify the example above.
import java.lang.reflect.Field; enum A1{ A,B } enum A2{ C,D } public class SampleClass{ private static A1 a1 = A1.A; public static void main(String[] args) throws Exception{ Field f = SampleClass.class.getDeclaredField("a1"); if(A2.class.isAssignableFrom(A1.class)) f.set(f,A2.C); else System.out.println("Incompatible!"); } } Result Incompatible!