2

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:

  • Approach 1 - primaryConstructor:
val constructor = Student::class.primaryConstructor
  • Approach 2 - All constructors declared in the class:
// 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.

4
  • "but it should not be necessary such detour" Why not? You only see the parameterless constructor if you use javap, which is a Java thing. That's unrelated to the Kotlin reflection API. Commented Feb 22, 2022 at 11:49
  • @Sweeper createInstance() is part of Kotlin Reflectionm API and it may claim IllegalArgumentException: Class should have a single no-arg constructor. All this in Kotlin! IMO, it should also provide a way in Kotlin to let me know if I can make a call to createInstance() without exceptions. Commented Feb 22, 2022 at 11:53
  • Are you confused why createInstance doesn't throw an exception for Student? Have you read its documentation? It also checks if the parameters are optional, not just whether the class has a parameterless constructor. Commented Feb 22, 2022 at 12:00
  • @Sweeper I am not confused about that. As I am clarifying at the end of OP: "we would like to check before-hand if we could call the createInstance() with no exceptions.". I do not want to use a try/catch to check if the createInstance() works fine, or not, and if we have to choose a different way of instantiating dynamically. Commented Feb 22, 2022 at 12:04

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.