Overloading the Primary Constructor in Kotlin for Java Interoperability
One of the great features of Kotlin is its interoperability with Java code. This allows you to easily call 'traditional' Java code from your Kotlin code, but it also helps you the other way around: calling Kotlin code from Java.
Sometimes, a little extra work is needed to make some shiny Kotlin feature work with Java code. For example,
Kotlin supports default parameter values, which are not supported in Java. In this case, the @JvmOverloads
annotation
can be used to generate overloads for functions that contain parameters with default values.
This annotation does not only work on functions, but can also be applied on constructors. In this post I will explain how
to use this feature on the primary constructor, as it might be confusing where to place the annotation.
Short recap: when placing the @JvmOverloads
annotation on a function, the compiler will automatically generate overloads
for the function, when any parameter has a default value. For example, given the following function:
@JvmOverloads
fun myFunction(arg1: Int = 1, arg2: String = "x", arg3: Boolean = true) {
// ...
}
The function can be called in any of the following ways from Java code:
myFunction(5, "y", false); // Parameter values: [arg1: 5, arg2: "y", arg3: false]
myFunction(5, "y"); // Parameter values: [arg1: 5, arg2: "y", arg3: true]
myFunction(5); // Parameter values: [arg1: 5, arg2: "x", arg3: true]
myFunction(); // Parameter values: [arg1: 1, arg2: "x", arg3: true]
However, say we have a data class containing persons. We want to be able to construct a Person
object with a firstName
,
optionally specifying the lastName
and birthday
parameters. We could model it like this:
data class Person(
val firstName: String,
val lastName: String? = null,
val birthday: LocalDate? = null,
)
Where do we place the annotation? We cannot place the annotation on the line above the class declaration, because it would be interpreted as a class-level annotation.
Solution: explicitly use the constructor
keyword on the primary constructor, and place the annotation in front of that keyword.
data class Person @JvmOverloads constructor(
val firstName: String,
val lastName: String? = null,
val birthday: LocalDate? = null
)
This will allow you to create instances of this class from Java code in any of the following ways:
new Person("x"); // Object values: [firstName: "x", lastName: null, birthday: null]
new Person("x", "y"); // Object values: [firstName: "x", lastName: "y", birthday: null]
new Person("x", "y", LocalDate.of(1980, Month.JANUARY, 1)); // Object values: [firstName: "x", lastName: "y", birthday: "1980-01-01"]
Explicitly using the constructor keyword can also be used to add a visibility modifier specifically to the constructor,
e.g.: class MyClass private constructor(myArg: String)
|
Compiled and tested with Kotlin version 1.7.10.