Invoking Constructors
A Constructor
object lets you get more information on the corresponding constructor: its modifiers, the types and names of its parameters, and enables you to invoke to create instance of objects, passing the arguments you need. This section also covers how you can discover constructors in a class.
Locating Constructors
There are two categories of methods provided in Class
for accessing constructors.
- First, you can look for a specific constructor. These methods suppose you have the type of its parameter.
- Second, you can either look for all the constructors that are declared in this class. In that case, you will get all the constructors: public, protected, default (package) access, and private. Or you can look for the public constructors only.
Note that constructors are not inherited, so you do not have any equivalent to the methods you saw on fields or methods, to locate elements on inherited classes.
Class API | Array of constructors? | Private constructors? |
---|---|---|
getDeclaredConstructor(Class...) |
no | yes |
getConstructor(Class...) |
no | no |
getDeclaredConstructors() |
yes | yes |
getConstructors() |
yes | no |
A constructor declaration includes the name, modifiers, parameters, and list of throwable exceptions. The java.lang.reflect.Constructor
class provides a way to obtain this information.
Let us see this method in action. The following code queries the constructors of the String
class.
The following code prints the 19 constructors you have in this class in the JDK 23. Note that not all of them are public.
Class<?> c = String.class;
Constructor<?>[] constructors = c.getDeclaredConstructors();
System.out.println("# constructors = " + constructors.length);
for (Constructor<?> constructor : constructors) {
System.out.println("constructor = " + constructor);
}
Running this code produces the following.
# constructors = 19
constructor = public java.lang.String(java.lang.StringBuilder)
constructor = private java.lang.String(char[],int,int,java.lang.Void)
constructor = java.lang.String(java.lang.AbstractStringBuilder,java.lang.Void)
constructor = private java.lang.String(java.nio.charset.Charset,byte[],int,int)
constructor = public java.lang.String(byte[],int,int,java.nio.charset.Charset)
constructor = public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
constructor = public java.lang.String(byte[],java.nio.charset.Charset)
constructor = public java.lang.String(byte[],int,int)
constructor = public java.lang.String(byte[])
constructor = public java.lang.String(java.lang.StringBuffer)
constructor = java.lang.String(byte[],byte)
constructor = public java.lang.String(char[],int,int)
constructor = public java.lang.String(char[])
constructor = public java.lang.String(java.lang.String)
constructor = public java.lang.String()
constructor = public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
constructor = public java.lang.String(byte[],int)
constructor = public java.lang.String(byte[],int,int,int)
constructor = public java.lang.String(int[],int,int)
You can also locate a specific constructor, for instance, the one that takes an array of bytes. Note that the syntax byte[].class
is used to denote the class that corresponds to an array of bytes.
Class<?> c = String.class;
Constructor<?> constructor = c.getConstructor(byte[].class);
System.out.println("constructor = " + constructor);
Running this code produces the following.
constructor = public java.lang.String(byte[])
Obtaining the Parameters of a Constructor
The Constructor
class gives you access to the parameters of a given constructor. As for methods, you can get access to the raw type with the method Constructor.getParameterTypes()
method, or the generic type, with the Constructor.getGenericParameterTypes()
method.
Let us see these two methods in action by querying the constructors of the ArrayList
class.
First, let us find the raw types of the parameters.
Class<?> c = ArrayList.class;
Constructor<?>[] constructors = c.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("constructor with " + constructor.getParameterCount() + " parameter");
for (Class<?> type : constructor.getParameterTypes()) {
System.out.println(" " + type.getName());
}
}
Running this code produces the following.
constructor with 1 parameter
java.util.Collection
constructor with 0 parameter
constructor with 1 parameter
int
You can get the generic types with the following code. Note that the Constructor.getGenericParameterTypes()
method returns an array of Type
, on which you can call Type.getTypeName()
.
Class<?> c = ArrayList.class;
Constructor<?>[] constructors = c.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("constructor with " + constructor.getParameterCount() + " parameter");
for (Type type : constructor.getGenericParameterTypes()) {
System.out.println(" " + type.getTypeName());
}
}
Running this code produces the following.
constructor with 1 parameter
java.util.Collection<? extends E>
constructor with 0 parameter
constructor with 1 parameter
int
Obtaining Exceptions of a Constructor
The Constructor
class gives you access to the declared exceptions of a Constructor. These exceptions are returned in an array, which can be of two types: an array of Class
instances, or an array of Type
instances. As usual the Type
object can give you information about a parameter type.
Let us discover the exceptions thrown by the constructor of FileWriter
that takes a String
as a parameter.
Class<?> c = FileWriter.class;
Constructor<?> constructor = c.getConstructor(String.class);
for (Class<?> exceptionType : constructor.getExceptionTypes()) {
System.out.println("Exception type: " + exceptionType.getName());
}
for (Type exceptionType : constructor.getGenericExceptionTypes()) {
System.out.println("Generic exception type: " + exceptionType.getTypeName());
}
Running this example produces the following:
Exception type: java.io.IOException
Generic exception type: java.io.IOException
Invocation Patterns
Invoking a constructor creates a new instance of a class. There are two reflective methods for creating instances of classes: Class.newInstance()
and Constructor.newInstance()
, which is the pattern you should use.
The first pattern consists in calling the newInstance()
method of the Class
. This method is actually calling the no-arg constructor of the class it is invoked on. If this constructor does not exist or is not visible (for instance: it is private), then it throws an exception.
The Class.newInstance()
method has been deprecated in Java SE 9, and should not be used anymore. There are several reasons for that.
Class.newInstance()
can only invoke the zero-argument constructor, whileConstructor.newInstance()
may invoke any constructor, regardless of the number of parameters.Class.newInstance()
throws any exception thrown by the constructor, regardless of whether it is checked or unchecked.Constructor.newInstance()
always wraps the thrown exception with anInvocationTargetException
. So it bypasses the compile-time exception checking, reason why it has been deprecated.Class.newInstance()
requires that the constructor be visible;Constructor.newInstance()
may invoke private constructors under certain circumstances.
The second pattern that you should be using now, consists in getting a reference on one of the constructor of the class, and invoke it.
Creating an Instance of String
The following example creates an instance of String
.
Class<String> stringClass = String.class;
String emptyString = stringClass.getConstructor().newInstance();
System.out.println("Empty? " + emptyString.isEmpty());
Running this code prints the following:
Empty? true
Note the following:
stringClass.getConstructor()
throws aNoSuchMethodException
, in the case the class does not have a no-arg constructor.- the call to
newInstance()
throws several exception:InvocationTargetException
: wrapping the exception the invocation of the constructor may have thrown.InstantiationException
: in case the corresponding class cannot be instantiated. This is the case for abstract classes or interfaces.IllegalAccessException
: in case the no-arg constructor is not accessible from the calling code. This is the case if the constructor is private, dans is accessed from another class.
Creating an Instance of a Record
Suppose that you have the following record.
public record Point(int x, int y) {
public Point() {
this(0, 0);
}
}
Then you can invoke its empty constructor with the following code.
Class<Point> c = Point.class;
Constructor<Point> constructor = c.getConstructor();
Point p = constructor.newInstance();
System.out.println("p = " + p);
Running the previous code prints the following.
p = Point[x=0, y=0]
You can also invoke its canonical constructor with the following code. Note that, despite it is not explicitly written in the code, the compiler generates this canonical constructor for you. You can learn more on records in this section.
Class<Point> c = Point.class;
Constructor<Point> constructor = c.getConstructor(int.class, int.class);
Point p = constructor.newInstance(1, 1);
System.out.println("p = " + p);
Running the previous code produces the following result.
p = Point[x=1, y=1]
Last update: July 25, 2024