All you need to know about Java Reflection – Introduction

Serene night landscape with a solitary tree reflecting in calm waters beneath a starry sky.

What is Java Reflection

Java reflection is a feature that lets you examine or modify the behavior of Java classes, methods, and interfaces during runtime. With this Java feature you can, for example, get information about a class’s fields, methods, and constructors, change field values, invoke methods, and instantiate objects. Those features are available through Reflection API, which is a set of classes and methods that come with every JDK. Java Reflection is widely used in popular Libraries and Frameworks, such as JUnit, Spring, Google Guice, Jackson and Gson; it’s also used in Logging Frameworks, ORM (Object Relational Mapping) tools, Web Frameworks, Developer tools, and many more.

Beware that Reflection is a powerful tool that, if used incorrectly or not for the right purposes, can make the code harder to maintain, slower to run, and dangerous to execute, as it may cause your program to crash.

Reflection entry point: Class<?>

An object of type Class<?> contains all the information about a given object’s runtime type and class of the application. Some of those information are: what methods and fields it contains, what classes it extends, and what interfaces it implements. There are three way to get the Class<?> object. Let’s see each of them.

1 – Object.getClass()

The first method is to use the getClass() from inherited from Object class.

String str = "some string";
Person person = new Person();
Map<String, Integer> map = new HashMap<>;

Class<String> stringClass = str.getClass();
Class<Person> personClass = person.getClass();
Class<?> mapClass = map.getClass();

Notice that the the last class represents the HashMap class, not the Map interface, since that’s the runtime type of that variable. Notice also that primitive types are not objects, therefore, you cannot call the getClass() method on them; doing so will cause a compilation error.

2 – .class suffix to a type name

The second way to get a Class<?> object is to append the .class suffix to a type. This method is used when we don’t have an instance of a particular class but we still want to get information of that class.

Class str = String.class;
Class person = Person.class;

Class booleanType = boolean.class;
Class intType = int.class;

Note from the snippet above that with this method we can also get the type information for primitive types. This method is going to be useful whenever we want to inspect method parameter or class methods, which can also be primitives.

3 – Class.forName(…)

Finally, the third method is to use the static forName() method from Class. With this method, we can dynamically lookup any class in the classpath using the fully qualified name of the class.

Class<?> strType = Class.forName("java.lang.String");
Class<?> personType = Class.forName("person.Person");

Class<?> engineType = Class.forName("vehicles.Car$Engine");

Note from the last example that we can also get information about inner classes using $ between the parent class and the inner class:

package vehicles;

Class Car {
    ...
    static Class Engine {
    ...
    }
}

Also, just like in the first method, we cannot use forName() with primitive types, but this time, instead of a compilation error, we’ll get a runtime error. Besides, we can get a ClassNotFoundException is the type doesn’t exist. Because of that, out of the three methods, this is the least safest to get a Class<?> object. However, there are a few use cases where this is the only option, like when the type we want to inspect or create an instance of is passed through a configuration files:

<bean id="vehicle" class="vehicles.Car">
    <property name="numberOfWheels" value="4" />
</bean>

Using this approach, one can modify the application’s behavior, like changing the class attribute to “vehicles.Motorcycle”, by just changing the configuration file, without needing to change or recompile the source code. This way, we can read the fully qualified name from the configuration file and get the Class object of that type at runtime.

The other use case is when the class we want to reflect on is not part of our project and doesn’t exist when we compile the code, but this class is added to the classpath at runtime; this is useful when we are creating a library the we’ll later import and use.

Getting Class using the three possible methods

Now let’s see a full example in which we use the three possible methods to get the Class object.

package org.example.reflection;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class Main {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<String> str = String.class;

        Map<String, Integer> map = new HashMap<>();
        Class<?> mapClass = map.getClass();

        Class<?> squareClass = Class.forName("org.example.reflection.Main$Square");

        Drawable circle = new Drawable() {
            @Override
            public int getNumberOfCorners() {
                return 0;
            }
        };

        printClassInfo(str, mapClass, squareClass, Collection.class, boolean.class, int[][].class, Color.class, circle.getClass());
    }

    private static void printClassInfo(Class<?>... classes) {
        for (Class<?> clazz : classes) {
            System.out.println(String.format("class name: %s; class package name: %s",
                    clazz.getSimpleName(), clazz.getPackageName()));

            Class<?>[] implementedInterfaces = clazz.getInterfaces();

            for (Class<?> implementedInterface : implementedInterfaces) {
                System.out.println(String.format("class %s implements interface %s",
                        clazz.getSimpleName(), implementedInterface.getSimpleName()));
            }
            System.out.println("is array: " + clazz.isArray());
            System.out.println("is primitive: " + clazz.isPrimitive());
            System.out.println("is enum: " + clazz.isEnum());
            System.out.println("is interface: " + clazz.isInterface());
            System.out.println("is anonymous: " + clazz.isAnonymousClass());

            System.out.println();
        }
    }

    private static class Square implements Drawable {
        @Override
        public int getNumberOfCorners() {
            return 4;
        }
    }

    private static interface Drawable {
        int getNumberOfCorners();
    }

    private enum Color {
        RED,
        GREEN,
        BLUE
    }

}

See the output, and next let’s break down this program:

class name: String; class package name: java.lang
class String implements interface Serializable
class String implements interface Comparable
class String implements interface CharSequence
class String implements interface Constable
class String implements interface ConstantDesc
is array: false
is primitive: false
is enum: false
is interface: false
is anonymous: false

class name: HashMap; class package name: java.util
class HashMap implements interface Map
class HashMap implements interface Cloneable
class HashMap implements interface Serializable
is array: false
is primitive: false
is enum: false
is interface: false
is anonymous: false

class name: Square; class package name: org.example.reflection
class Square implements interface Drawable
is array: false
is primitive: false
is enum: false
is interface: false
is anonymous: false

class name: Collection; class package name: java.util
class Collection implements interface Iterable
is array: false
is primitive: false
is enum: false
is interface: true
is anonymous: false

class name: boolean; class package name: java.lang
is array: false
is primitive: true
is enum: false
is interface: false
is anonymous: false

class name: int[][]; class package name: java.lang
class int[][] implements interface Cloneable
class int[][] implements interface Serializable
is array: true
is primitive: false
is enum: false
is interface: false
is anonymous: false

class name: Color; class package name: org.example.reflection
is array: false
is primitive: false
is enum: true
is interface: false
is anonymous: false

1 – We have a Main class and, inside of it, we declared an enum Color, a functional interface Drawable, a static inner class Square that implements Drawable, a printClassInfo method that takes a vargars of Class<?> as argument, and a main method used to test the code.

2 – We iterate over each Class passed to printClassInfo and print information like the implemented interfaces and whether or not it is a primitive type.

3 – The output shows correctly all the interfaces implemented by String, like Serializable and Comparable; the same thing happens with the HashMap class.

4 – Note that to get the Square class we had to use the $ after its outer class, Main.

5 – The output also shows correctly that Collection is an interface that extends Iterable, boolean is a primitive, int[][] is an array, and Color is an enum.

6 – Finally, circle is an object of an anonymous class, thats why its class name is empty and is anonymous is true. (Notice that the anonymous expression new Drawable() can be replaced with lambda, as Drawable is a functional interface.)

This finalizes the Introduction of our series of articles about Java Reflection.

1 thought on “All you need to know about Java Reflection – Introduction”

  1. Pingback: All you need to know about Java Reflection – Constructors - Java Spring Works

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top