Given a domain class with a parameterless constructor, how do we get a reference to that constructor through the Reflection API?
Consider for example a Student
data class, such as:
data class Student(var nr: Int = 0, var name: String? = null)
Notice, we can confirm the presence of the parameterless constructor through javap
that shows:
public pt.isel.Student(int, java.lang.String);
descriptor: (ILjava/lang/String;)V
public pt.isel.Student(int, java.lang.String, int, kotlin.jvm.internal.DefaultConstructorMarker);
descriptor: (ILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public pt.isel.Student();
descriptor: ()V
Yet, none of the following approaches returns the parameterless constructor:
primaryConstructor
:val constructor = Student::class.primaryConstructor
// java.util.NoSuchElementException: Collection contains no element matching the predicate
val constructor = Student::class.constructors.first { it.parameters.isEmpty() }
Alternatively, we can proceed via Java Reflection that works fine, but it should not be necessary such detour:
val constructor = Student::class.java.getDeclaredConstructor()
Second, why do we need that? Because we want to instantiate a domain class at runtime. Yes, we know that createInstance()
of KClass
do that job. But it throws IllegalArgumentException: Class should have a single no-arg constructor
if there is no parameterless constructor.
Thus, we would like to check before-hand if we could call the createInstance()
with no exceptions.
The parameterless constructor here only exists in the compiled Java class, and not in your Kotlin code. As far as Kotlin code is concerned, your Student
class has one single constructor, with 2 optional parameters.
The Kotlin reflection API is designed to be platform-independent, so you have to use Java reflection to get the parameter constructor.
If you just want to see if you can call createInstance
safely, you can just check if the class has a single constructor whose parameters are all optional. This is documented:
Creates a new instance of the class, calling a constructor which either has no parameters or all parameters of which are optional. If there are no or many such constructors, an exception is thrown.
val isSafe = someClass.constructors.singleOrNull {
it.parameters.all(KParameter::isOptional)
} != null
This is similar to how createInstance
is implemented to throw the exception.