Last Updated:

A complete guide to the Java Reflection API | Reflection on examples

Reflection in Java is a mechanism by which you can make changes and get information about classes, interfaces, fields, and methods at run time without knowing the names of those classes, methods, and fields. In addition, the Reflection API allows you to create new instances of classes, call methods, and get or set field values.

Novice Java programmers often confuse reflection with introspection. Introspection is the ability to inspect code and see the types of objects at run time. Reflection makes it possible to make changes during the execution of the program through the use of introspection. It is important to understand the difference here, as some languages support introspection but do not support reflection, such as C++.

In this tutorial, we'll look at not only the basic, but also the more advanced features of the Reflection API.

Java Reflection API

Reflection is a powerful concept that underlies most modern Java/Java EE frameworks and libraries. For example, for Java, the classic examples are:

  • JUnit is a framework for unit testing. He uses reflection to parse annotations (for example, ) to obtain the test methods described by the programmer and then execute them.@Test
  • Spring is a framework for developing applications on the Java platform, which is based on the introduction of dependencies (control inversion).

The list goes on and on, from web containers to object-relational mapping (ORM) tasks. They all have one thing in common: they use Java reflection because they do not have access and representation to user-defined classes, methods, interfaces, etc.

Limitations when working with reflection in Java

Why shouldn't we use reflection in normal programming when we already have access to interfaces and classes. There are several reasons:

  1. Poor performance — Because java's reflection determines types dynamically, it scans the classpath to find the class to load, resulting in slow program performance.
  2. Security Restrictions – Reflection requires runtime permissions that cannot be accessed by systems running the Java Security Manager.
  3. Application security breaches – with reflection, we can access a piece of code that we shouldn't be able to access. For example, we can access the private fields of a class and change their values. This can be a serious security threat.
  4. Difficulty in maintaining – Code written through reflection is difficult to read and debug, making it less flexible and difficult to maintain.

Java Reflection: Working with Classes

The object is the entry point for all reflection operations. For each object type, the JVM creates an immutable instance that provides methods for retrieving the object's properties, creating new objects, and calling methods.java.lang.Classjava.lang.Class

In this section, we'll look at important techniques when working with:java.lang.Class

  • All types in Java, including primitive types and arrays, have an object associated with them. If we know the name of the class at compile time, we can get the object as follows:java.lang.Class

 

  • If we don't know the name at compile time, but we know the name of the class at run time, we can do this:

When using the method, we must specify the fully qualified name of the class. That is, the name of the class, including all package names. For example, if it is in the package , then the fully qualified name of your class is the string: .Class.forName()SomeObjectcom.javadevblog.appcom.javadevblog.app.SomeObject

The method can throw an exception if the class is not found at run time.Class.forName()ClassNotFoundExceptionclasspath

We get the name of the class

With an object, we can get the class name in two ways:Class

With a method that will return the fully qualified name of the class with the package:getName()

And using the method , which will return only the class name without the package name:getSimpleName()

 

Working with Access Modifiers

We can access access the access modifiers using an object. Modifiers are keywords, etc. We can get modifiers using the :

Classpublic, static, privategetModifiers()

The result of the execution is in the variable , where each modifier is a bit flag that can be set or reset. We can test modifiers using the following methods in the class:intjava.lang.reflect.Modifier

Modifier.isAbstract(int modifiers)
Modifier.isFinal(int modifiers)
Modifier.isInterface(int modifiers)
Modifier.isNative(int modifiers)
Modifier.isPrivate(int modifiers)
Modifier.isProtected(int modifiers)
Modifier.isPublic(int modifiers)
Modifier.isStatic(int modifiers)
Modifier.isStrict(int modifiers)
Modifier.isSynchronized(int modifiers)
Modifier.isTransient(int modifiers)
Modifier.isVolatile(int modifiers)

In the parameters of the method just pass modifiers and each of these methods will return or .truefalse

Retrieving Package Information

Knowing only the class we can get information about the package:

We can also access the information specified for this package in the file inside the JAR file. this package is in the classpath. Read more about it in of. Documentation: java.lang.Package.ManifestPackage

Get Superclass

By knowing an object, we can access its superclass:Class

If we get a superclass with the help of reflection, we can work with it, as with any other object.

Implemented interfaces

The list of interfaces implemented by this class can be obtained as follows:

A class can implement many interfaces. Therefore, an array of objects is returned. In the Java Reflection API, the interfaces are also represented by .ClassClass

Note: The method returned only the interfaces that the specified class implements, not its superclass. In order to get a complete list of interfaces implemented in this class, you must refer to both the current class and its superclass.

Reflection in Java: Constructors

By using the Java Reflection API, you can get information about class constructors and create objects at run time. This is done using the Java class.

java.lang.reflect.Constructor

Getting the Class Constructors

 

In the code above, we got an array of class constructors and can now work with them. If the parameters of a particular constructor are known, then the array can not be obtained, but work with a known one. For example, the following class constructor takes a String as a parameter:

If we make a mistake with the constructor's argument, then an exception will be thrown.NoSuchMethodException

Get the designer parameters

In the examples above, we got an array of constructors. Now we can find out the parameters for each of them. For example, you can get the parameter types of a constructor as follows:

 

Creating Objects Using Constructors

Now that we have the class constructors and know their remeters, we can create a new instance of the specified class:

The creation of a new instance of the class occurred after the method was called on the class constructor.newInstance()

Reflection in Java: Fields

Using reflection, we can work with fields— the member variables of a class. The Java class helps us with this: with it in the runtime we can set values and get data from fields.java.lang.reflect.Field

Get the class fields

You can get the fields of a class by using reflection by using the following line of code:

Each element of the array contains an instance of the public field declared in the class.

If you know the name of the field you want to access, just call the following line:

The line above will return the value of a field named fieldName with the type of our SomeObject class:Field

If we specify an incorrect field name, we get .NoSuchFieldException

Get the name of the field

Get the field type

Get and set field values

The parameter that is passed to the methods to get and set the value of the field must be an instance of the class to which the field itself belongs. The example above uses an instance of the class , because the field is a member of an instance of that class.instanceSomeObjectfieldName

Java Reflection API: Methods

Using reflection in Java, we can get information about methods and call them in the runtime. To do this, use the class :java.lang.reflect.Method

We don't need to get an array with all the methods if we know the exact parameter types of the method we want to use. For example, we have a method called "" that takes as a parameter. You can get an object for it as follows:sayHelloStringMethod

If there is no such method, then .NoSuchMethodException

If the method is without parameters, then you need to pass in the method :

sayHello()nullgetMethod()

 

Method parameters and return types

You can get the parameters of the method as follows:

In the line below, we can get the type of the return value:

 

To call a method using Java Reflection

In the code above, we got a method of the specified class and called the .invoke()

  • objectToInvokeOn is of type Object and is the object on which we want to call the .sayHello()
  • parameterTypes — of type Class[] and represents the parameters that the method takes as input.
  • params is of type Object[] and represents the perameters that are passed to the methods.

It should be noted that if we know that the sayHello method is static, then the call to the method using reflection can be rewritten as follows:

That is, instead of the objectToInvokeOn call object, specify null.

Java Reflection API: Methods for Setting and Retrieving a Value

In addition to the usual class methods, we can also work with the get and set methods using reflection. There are several ways to get these methods: explicitly specify a getter or setter when running, or bypass all the methods available in the class and check them for get or set methods:

To define a getter or setter, we need to define some rules:

  • A getter is a method whose name begins with 'get'. It does not accept arguments and necessarily returns some value.
  • A setter is a method whose name begins with 'set'. It takes 1 parameter.

Typically, setters do not return a value, but when the method is separated from the setter, the returned parameter is ignored.

Let's look at an example of defining a method as a setter or a getter using reflection in Java:

public static void printGettersOrSetters(Class aClass){
Method[] methods = aClass.getMethods();
for(Method method : methods){
if(isGetter(method)) System.out.println("getter: " + method);
if(isSetter(method)) System.out.println("setter: " + method);
}
}
public static boolean isGetter(Method method){
if (!method.getName().startsWith("get")) {
return false;
}
if (method.getParameterTypes().length != 0) {
return false;
}
if (void.class.equals(method.getReturnType()) {
return false;
}
return true;
}
public static boolean isSetter(Method method){
if (!method.getName().startsWith("set")) {
return false;
}
if (method.getParameterTypes().length != 1) {
return false;
}
return true;
}

With the help of reflection in Java, you can access the private fields of other classes. This is very convenient, for example, for unit testing.

Please note that retrieving private fields works correctly only in standalone Java applications. If you try to access the private fields inside the Java applet, you will need to mess with the Java SecurityManager.

Accessing private fields through reflection

To access a private field, you will need to use a method or method. Methods and return only public fields, so in our case they will not work.

Class.getDeclaredField(String name)

Class.getDeclaredFields()

Class.getField(String name)Class.getFields()

Let's use an example of how to work with private fields using Java Reflection:

We have a class with a closed field. Now let's write the code with which we will access it:PrivateClassmPrivateString

In the code above, we used the method , which has access only to the fields declared in a particular class and does not have access to the fields of the superclass.getDeclaredField

Note that in the code above we used , which disables access checking for the specified field. Now we can work with him through reflection, even if he had private, protected or default access. Without the use of reflection, this method is still private and the compiler will not allow us to access it.Field.setAcessible(true)

Accessing Private Methods Through Reflection

The methods of working with methods already known to us will not help here, since they have access only to public methods.Class.getMethod(String name, Class[] parameterTypes)Class.getMethods()

Private methods are accessed using methods or .Class.getDeclaredMethod(String name, Class[] parameterTypes)Class.getDeclaredMethods()

Now let's look at a way to get a private method in Java using reflection. Let's add a private method to the class created above:getPrivateString()PrivateClass

Now let's write code that will use reflection to access this method:private

As well as private fields, we can only access the private methods of the current class (without access to the private methods of the superclass).

With the help, we remove the access check only for reflection.Method.setAcessible(true)

Java Reflection: Annotations

With The help of Java reflection we can process annotations in the runtime.

What are annotations in Java. Brief description and example of work.

Annotations can also be processed through reflection. For a complete Java Reflection tutorial, let's create a simple annotation and then learn how to handle it with reflection:

With the help, we indicate that we are describing the annotation.@

We also used two annotations that apply only to annotations when creating:

  • @Retention(RetentionPolicy.RUNTIME) and specify how the custom annotation will be used.@Target(ElementType.TYPE)
  • @Retention(RetentionPolicy.RUNTIME) indicated that the annotation will be used in the rantime, so we can access it through reflection.
  • @Target(ElementType.TYPE) indicates that we can use annotation on interfaces and classes.

Now let's "hang" our annotation on some class:

With reflection, we can work with annotations of a class, method, or field in the runtime. Let's now get all the class annotations (in our case, we only have 1 of our annotation):SimpleClass

In the code above, we used the method to get all the annotations of the class as an array of . In the loop through all the annotations, we look for a custom annotation — Reflectable — and get information about it.getAnnotations()Annotation

If we are interested in a specific annotation, then you can access it as follows:

Working with annotations on methods

Annotations on methods work exactly as well as in the example above, the only difference is in getting a list of annotations. If above we used the method , then for annotations on methods you can call the method - get all annotations from the specified method.getAnnotations()getDeclaredAnnotations()

Our class with an annotation on the method:

Using reflection:

Or we explicitly get from this method (if we know for sure that it is on this method):

Annotations on method parameters

Let's add another method to our class with one parameter marked with an annotation:sayBye()

Now let's write the code to get the annotations on the parameters of the method:

In the code above, we got a two-dimensional array of annotations on the method: the internal array of each element is a set of annotations for each parameter (method argument).

Annotations in the margin

Let's add a field to our class: let it be a string with a null value:

Now using reflection, we get annotations to the field:

Class mClassObject = SimpleClass.class
Field field = mClassObject.getField("mField");
Annotation annotation = field.getAnnotation(Reflectable.class);

In various articles and forums, it is often written that all information about generics in Java is erased at compile time and we cannot get it at runtime (the so-called type erasure). This statement is not entirely true. The ability to obtain information about generics at runtime is in several cases. Let's take a look at them below.

Typically, generics in Java are used in the following situations:

  • When declaring a parameterized class or interface.
  • Use a parameterized class.

An example is the interface. Instead of creating a list of objects, you can parameterize it with, say, .java.util.ListString

While the program is running, there is no way to see the type of parameterized, but we can find it in the fields and methods where it is used and parameterized using Java reflection. Let's look at an example:java.util.List

Information about generics in rantime

We can get information about the type using the class .java.lang.reflect.Method

Below is the class we'll be processing:

You can get information about generics of a parameterized list if you work not with the list itself, but with the getter method:getList()

Will be printed in the console: type: java.lang.String.

In the code above, we defined that it is not just , but .ListList<String>

If we have a field, then we can get information about the type in the runtime as follows:

Will be printed in the console: type: java.lang.String. In the code above, we defined that the field type is parameterized (using ) and from it we have already obtained an array of types that contains 1 element of the type (it implements the interface ). So we just cast it to and printed it into the console.instanceofType[]ClassTypeClass

Java Reflection API: Arrays

Working with arrays is the most difficult, especially if we want to get an object for an array of some , for example. You can get information about arrays by using a class (not to be confused with the Java collection packageClassint[]java.lang.reflect.Arrayjava.util.Arrays)

Creating an array with 2 elements looks like this:int

In the first parameter of the method, specify the type of elements of the array, in the second - the number of elements of this array.newInstance()

Now let's look at the work of gatekeepers and setters when working with arrays using Java reflection:

int[] simpleIntArray = (int[]) Array.newInstance(int.class, 2);
Array.set(simpleIntArray, 0, 443);
Array.set(simpleIntArray, 1, 554);
System.out.println("first array element = " + Array.get(simpleIntArray, 0));
System.out.println("second array element = " + Array.get(simpleIntArray, 1));

With the use of reflection, this looks quite difficult:

Note the parameter: :"[I"

  • The JVM represents the int type using the ."I"
  • The character "[" on the left represents the int array class.

This works for all primitives. But for objects, the situation is different. Let's look at an example:String

The combination at the beginning is an array of objects of a certain type (the type is described between them)."[L"";"

Such difficulties give rise to other difficulties - already with the convenience of work. To circumvent this, the following helper methods are usually written:

And now create an instance of :Class

Get the array type

Getting the array type is just as simple:

And we'll get in the console.java.lang.String

This is the end of the Java Reflection Guide. Subscribe and follow the updates: you will find a lot of new materials on modern development in Java and Android.