Thursday, March 17, 2022

Java Tutorial: Reflection

Chapters

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.String
If 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.String
when .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
boolean
There 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: MyString
Use 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: int
In 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! | 5
If 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
d
Another 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
8
Another 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
true
In 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!