All you need to know about Java Reflection – Constructors

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

We saw in the introduction of this series of articles about Java Reflection what exactly is Reflection and what are the three ways we can get the Class object from a particular object or class. Now, let’s see how we can get information about the constructors of a class, and how we can use this information to create objects.

Getting information about constructors

In the code snippet below we can see the use of the method getDeclaredConstructors() from Class that returns an array of Constructor<?>; and the use of getParameterTypes() from Constructor, which returns an array of parameter types.

package org.example.reflection;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        printConstructorData(Person.class);
        printConstructorData(Address.class);
    }

    public static void printConstructorData(Class<?> clazz) {
        Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();

        System.out.println(String.format("Class %s has %d declared constructors", clazz.getSimpleName(), declaredConstructors.length));

        for (Constructor<?> declaredConstructor : declaredConstructors) {
            Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
            List<String> parameterTypeNames = Arrays.stream(parameterTypes)
                    .map(Class::getSimpleName)
                    .toList();
            System.out.println(parameterTypeNames);
        }
    }

    public static class Person {
        private final Address address;
        private final String name;
        private final int age;

        public Person() {
            this.address = null;
            this.name = "anonymous";
            this.age = 0;
        }

        public Person(String name) {
            this.address = null;
            this.name = name;
            this.age = 0;
        }

        public Person(String name, int age) {
            this.address = null;
            this.name = name;
            this.age = age;
        }

        public Person(Address address, String name, int age) {
            this.address = address;
            this.name = name;
            this.age = age;
        }

    }

    public static class Address {
        private final String street;
        private final int number;

        public Address(String street, int number) {
            this.street = street;
            this.number = number;
        }

    }

}

This is the output of this program:

Class Person has 4 declared constructors
[Address, String, int]
[String, int]
[String]
[]
Class Address has 1 declared constructors
[String, int]

Notice that the list of constructor begins with the most inclusive ones, and that the Address class doesn’t have a default constructor because we didn’t declare one. Remember that Java would automatically create a default constructor if we didn’t have declared any.

Generic object creation

We can create an instance of a class using reflection with the help of the Constructor’s method newInstance(Object… arguments). This method takes a variable number of arguments that represent the type and order of the constructor parameters. When called with success, this method will call the respective constructor and return an object; if the method call fails, it will throw an exception describing the reason of the failure.

package org.example.reflection;

import java.lang.reflect.Constructor;

public class Main {

    public static void main(String[] args) {
        Person person = createObject(Person.class);
        System.out.println(person);

        Address address = createObject(Address.class, "Main St", 123);
        System.out.println(address);

        Address invalidAddress = createObject(Address.class);
        System.out.println(invalidAddress);
    }

    public static <T> T createObject(Class<T> clazz, Object... arguments) {
        for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
            if (constructor.getParameterCount() == arguments.length) {
                try {
                    return (T) constructor.newInstance(arguments);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("Constructor not found");
        return null;
    }

    public static class Person {
        private final Address address;
        private final String name;
        private final int age;

        public Person() {
            this.address = null;
            this.name = "anonymous";
            this.age = 0;
        }

        public Person(String name) {
            this.address = null;
            this.name = name;
            this.age = 0;
        }

        public Person(String name, int age) {
            this.address = null;
            this.name = name;
            this.age = age;
        }

        public Person(Address address, String name, int age) {
            this.address = address;
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "address=" + address +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    public static class Address {
        private final String street;
        private final int number;

        public Address(String street, int number) {
            this.street = street;
            this.number = number;
        }

        @Override
        public String toString() {
            return "Address{" +
                    "street='" + street + '\'' +
                    ", number=" + number +
                    '}';
        }
    }

}

This is the output:

Person{address=null, name=’anonymous’, age=0}
Address{street=’Main St’, number=123}
Constructor not found
null

A few things to notice:

1 – As the first example, we also have the classes Person and Address, but here we override the toString() method.

2 – We wrote the factory method createObject(Class<?> clazz, Object… arguments): it takes a Class and a Varargs as arguments. Thanks to the versatility of the Varargs, we can pass a variable number or arguments, including zero; that’s how we created the Person object using the default constructor.

3 – Notice that, as mentioned before, we don’t have a default constructor for the type Address, therefore the program returns null.

4 – Also notice the use of Generics in the method createObject(); without it, we would need to return an Object and cast the result.

Calling private constructors with Reflection

It’s not possible to call a private constructor from outside of its class; if we try to do so, we’re going to get a compilation error. However, using Reflection we gain access to all types of constructors (public, protected, package-private, and private). This way, we can use the newInstance() method we’ve seen before to create an instance of a class using restricted constructors. The only exception to this is when the class we’re trying to access belongs to a Java module that does not allow access to that particular class. To make the restricted constructor available, we need to call the method setAccessible(true) of the class Constructor:

...
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
constructor.newInstance();
...

Beware that this feature should be restricted to specific use cases, it should not be used to invoke internal or deliberately restricted constructors that do not belong to us.

Package-private classes

As we know, classes that don’t have a modifier (public, private or protected) are called package-private, and are visible only to classes inside its package. Package-private classes can be used when we want to restrict internal classes from the package external users: by keeping only the classes that represent the package external API visible, the user of the package will not get confused which classes they should use and which ones only represent internal data models, helper classes, or implementation details.

Just like we did with the private constructor, to initialize the package-private class we just need to call the method setAccessible(true) of the Constructor class. One use case where we may want to instantiate a package-private class is a Dependency Injection implementation.

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

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

Leave a Comment

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

Scroll to Top