What is the Reflection API for?

Java's reflection mechanism allows a developer to make changes and obtain information about classes, interfaces, fields, and methods at runtime without knowing their names.

The Reflection API also lets you create new objects, call methods, and get or set field values.

Let's make a list of everything you can do using reflection:

  • Identify/determine the class of an object
  • Get information about class modifiers, fields, methods, constants, constructors, and superclasses
  • Find out which methods belong to implemented interface(s)
  • Create an instance of a class whose class name is not known until the program is executed
  • Get and set the value of an instance field by name
  • Call an instance method by name

Almost all modern Java technologies use reflection. It underlies most of today's Java / Java EE frameworks and libraries, for example:

  • Spring frameworks for building web applications
  • the JUnit testing framework

If you've never encountered these mechanisms before, you're probably asking why is all this necessary. The answer is quite simple but also very vague: reflection dramatically increases flexibility and the ability to customize our application and code.

But there are always pros and cons. So let's mention a few cons:

  • Violations of application security. Reflection lets us access code that we shouldn't (violation of encapsulation).
  • Security restrictions. Reflection requires runtime permissions that are not available to systems running a security manager.
  • Low performance. Reflection in Java determines types dynamically by scanning the classpath to find the class to load. This reduces the program's performance.
  • Difficult to maintain. Code that uses reflection is difficult to read and debug. It is less flexible and harder to maintain.

Working with classes using the Reflection API

All reflection operations begin with a java.lang.Class object. For each type of object, an immutable instance of java.lang.Class is created. It provides methods for getting object properties, creating new objects, and calling methods.

Let's look at the list of basic methods for working with java.lang.Class:

Method Action
String getName(); Returns the name of the class
int getModifiers(); Returns access modifiers
Package getPackage(); Returns information about a package
Class getSuperclass(); Returns information about a parent class
Class[] getInterfaces(); Returns an array of interfaces
Constructor[] getConstructors(); Returns information about class constructors
Fields[] getFields(); Returns the fields of a class
Field getField(String fieldName); Returns a specific field of a class by name
Method[] getMethods(); Returns an array of methods

These are the most important methods for getting data about classes, interfaces, fields, and methods. There are also methods that let you get or set field values, and access private fields. We'll look at them a little later.

Right now we'll talk about getting the java.lang.Class itself. We have three ways to do this.

1. Using Class.forName

In a running application, you must use the forName(String className) method to get a class.

This code demonstrates how we can create classes using reflection. Let's create a Person class that we can work with:

package com.company;

public class Person {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

And the second part of our example is the code that uses reflection:

public class TestReflection {
    public static void main(String[] args) {
        try {
            Class<?> aClass = Class.forName("com.company.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

This approach is possible if the full name of the class is known. Then you can get the corresponding class using the static Class.forName() method. This method cannot be used for primitive types.

2. Using .class

If a type is available but there is no instance of it, then you can get the class by adding .class to the type name. This is the easiest way to get the class of a primitive type.

Class aClass = Person.class;

3. Using .getClass()

If an object is available, then the easiest way to get a class is to call object.getClass().

Person person = new Person();
Class aClass = person.getClass();

What's the difference between the last two approaches?

Use A.class if you know which class object you are interested in at coding time. If no instance is available, then you should use .class.

Getting the methods of a class

Let's look at the methods that return the methods of our class: getDeclaredMethods() and getMethods().

getDeclaredMethods() returns an array that contains Method objects for all declared methods of the class or interface represented by the Class object, including public, private, default, and protected methods, but not inherited methods.

getMethods() returns an array containing Method objects for all public methods of the class or interface represented by the Class object — those declared by the class or interface, as well as those inherited from superclasses and superinterfaces.

Let's take a look at how each of them works.

Let's start with getDeclaredMethods(). To again help us understand the difference between the two methods, below we will work with the abstract Numbers class. Let's write a static method that will convert our Method array to List<String>:

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class TestReflection {
    public static void main(String[] args) {
        final Method[] declaredMethods = Number.class.getDeclaredMethods();
        List<String> actualMethodNames = getMethodNames(declaredMethods);
        actualMethodNames.forEach(System.out::println);
    }

    private static List<String> getMethodNames(Method[] methods) {
        return Arrays.stream(methods)
                .map(Method::getName)
                .collect(Collectors.toList());
    }
}

Here's the result of running this code:

byteValue
shortValue
intValue
longValue
float floatValue;
doubleValue

These are the methods declared inside the Number class. What does getMethods() return? Let's change two lines in the example:

final Method[] methods = Number.class.getMethods();
List<String> actualMethodNames = getMethodNames(methods);

Doing this, we will see the following set of methods:

byteValue
shortValue
intValue
longValue
float floatValue;
doubleValue
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll

Because all classes inherit Object, our method also returns the public methods of the Object class.

Getting the fields of a class

The getFields and getDeclaredFields methods are used to get the fields of a class. As an example, let's look at the LocalDateTime class. We'll rewrite our code:

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class TestReflection {
    public static void main(String[] args) {
        final Field[] declaredFields = LocalDateTime.class.getDeclaredFields();
        List<String> actualFieldNames = getFieldNames(declaredFields);
        actualFieldNames.forEach(System.out::println);
    }

    private static List<String> getFieldNames(Field[] fields) {
        return Arrays.stream(fields)
                .map(Field::getName)
                .collect(Collectors.toList());
    }
}

As a result of executing this code, we get the set of fields contained in the LocalDateTime class.

MIN
MAX
serialVersionUID
date
time

By analogy with our previous examination of methods, let's see what happens if we change the code a little:

final Field[] fields = LocalDateTime.class.getFields();
List<String> actualFieldNames = getFieldNames(fields);

Output:

MIN
MAX

Now let's figure out the difference between these methods.

The getDeclaredFields method returns an array of Field objects for all fields declared by the class or interface represented by this Class object.

The getFields method returns an array of Field objects for all public fields of the class or interface represented by the Class object.

Now let's look inside LocalDateTime.

The class's MIN and MAX fields are public, which means they will be visible through the getFields method. By contrast, the date, time, serialVersionUID methods have the private modifier, which means they will not be visible through the getFields method, but we can get them using getDeclaredFields. This is how we can access Field objects for private fields.

Descriptions of other methods

Now its time to talk about some methods of the Class class, namely:

Method Action
getModifiers Getting the modifiers for our class
getPackage Getting the package that contains our class
getSuperclass Getting the parent class
getInterfaces Getting an array of interfaces implemented by a class
getName Getting the fully qualified class name
getSimpleName Getting the name of a class

getModifiers()

Modifiers can be accessed using a Class object.

Modifiers are keywords like public, static, interface, etc. We get modifiers using the getModifiers() method:

Class<Person> personClass = Person.class;
int classModifiers = personClass.getModifiers();

This code sets the value of an int variable that is a bit field. Each access modifier can be turned on or off by setting or clearing the corresponding bit. We can check modifiers using the methods in the java.lang.reflect.Modifier class:

import com.company.Person;
import java.lang.reflect.Modifier;

public class TestReflection {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        int classModifiers = personClass.getModifiers();

        boolean isPublic = Modifier.isPublic(classModifiers);
        boolean isStatic = Modifier.isStatic(classModifiers);
        boolean isFinal = Modifier.isFinal(classModifiers);
        boolean isAbstract = Modifier.isAbstract(classModifiers);
        boolean isInterface = Modifier.isInterface(classModifiers);

        System.out.printf("Class modifiers: %d%n", classModifiers);
        System.out.printf("Is public: %b%n", isPublic);
        System.out.printf("Is static: %b%n", isStatic);
        System.out.printf("Is final: %b%n", isFinal);
        System.out.printf("Is abstract: %b%n", isAbstract);
        System.out.printf("Is interface: %b%n", isInterface);
    }
}

Recall what the declaration of our Person looks like:

public class Person {}

We get the following output:

Class modifiers: 1
Is public: true
Is static: false
Is final: false
Is abstract: false
Is interface: false

If we make our class abstract, then we have:

public abstract class Person {}

and this output:

Class modifiers: 1025
Is public: true
Is static: false
Is final: false
Is abstract: true
Is interface: false

We changed the access modifier, which means we also changed the data returned through the static methods of the Modifier class.

getPackage()

Knowing only a class, we can get information about its package:

Class<Person> personClass = Person.class;
final Package aPackage = personClass.getPackage();
System.out.println(aPackage.getName());

getSuperclass()

If we have a Class object, then we can access its parent class:

public static void main(String[] args) {
    Class<Person> personClass = Person.class;
    final Class<? super Person> superclass = personClass.getSuperclass();
    System.out.println(superclass);
}

We get the well-known Object class:

class java.lang.Object

But if our class has another parent class, then we will see it instead:

package com.company;

class Human {
    // Some info
}

public class Person extends Human {
    private int age;
    private String name;

    // Some info
}

Here we get our parent class:

class com.company.Human

getInterfaces()

Here's how we can get the list of interfaces implemented by the class:

public static void main(String[] args) {
    Class<Person> personClass = Person.class;
    final Class<?>[] interfaces = personClass.getInterfaces();
    System.out.println(Arrays.toString(interfaces));
}

And let's not forget to change our Person class:

public class Person implements Serializable {}

Output:

[interface java.io.Serializable]

A class can implement many interfaces. That's why we get an array of Class objects. In the Java Reflection API, interfaces are also represented by Class objects.

Please note: The method returns only the interfaces implemented by the specified class, not its parent class. To get a complete list of interfaces implemented by the class, you need to refer to both the current class and all its ancestors up the inheritance chain.

getName() & getSimpleName() & getCanonicalName()

Let's write an example involving a primitive, a nested class, an anonymous class, and the String class:

public class TestReflection {
    public static void main(String[] args) {
        printNamesForClass(int.class, "int class (primitive)");
        printNamesForClass(String.class, "String.class (ordinary class)");
        printNamesForClass(java.util.HashMap.SimpleEntry.class,
                "java.util.HashMap.SimpleEntry.class (nested class)");
        printNamesForClass(new java.io.Serializable() {
                }.getClass(),
                "new java.io.Serializable(){}.getClass() (anonymous inner class)");
    }

    private static void printNamesForClass(final Class<?> clazz, final String label) {
        System.out.printf("%s:%n", label);
        System.out.printf("\tgetName()):\t%s%n", clazz.getName());
        System.out.printf("\tgetCanonicalName()):\t%s%n", clazz.getCanonicalName());
        System.out.printf("\tgetSimpleName()):\t%s%n", clazz.getSimpleName());
        System.out.printf("\tgetTypeName():\t%s%n%n", clazz.getTypeName());
    }
}

Result of our program:

int class (primitive):
getName()): int
getCanonicalName()): int
getSimpleName()): int
getTypeName(): int

String.class (ordinary class):
getName()): java.lang.String
getCanonicalName()): java.lang.String
getSimpleName()): String
getTypeName(): java.lang.String

java.util.HashMap.SimpleEntry.class (nested class):
getName()): java.util.AbstractMap$SimpleEntry
getCanonicalName()): java.util.AbstractMap.SimpleEntry
getSimpleName()): SimpleEntry
getTypeName(): java.util.AbstractMap$SimpleEntry

new java.io.Serializable(){}.getClass() (anonymous inner class):
getName()): TestReflection$1
getCanonicalName()): null
getSimpleName()):
getTypeName(): TestReflection$1

Now let's analyze our program's output:

  • getName() returns the name of the entity.

  • getCanonicalName() returns the canonical name of the base class, as defined by the Java Language Specification. Returns null if the base class does not have a canonical name (that is, if it is a local or anonymous class or an array whose element type does not have a canonical name).

  • getSimpleName() returns the simple name of the base class as specified in the source code. Returns an empty string if the base class is anonymous.

  • getTypeName() returns an informative string for the name of this type.