Kotlin and Java interoperability

kotlin Apr 5, 2021

This is the third post in the "Kotlin, the essence of Java" series. Click the link for the whole story or keep on reading about this particular topic.

Kotlin is a JVM language with strong ties to the Java language and v.v. Under normal circumstances Java and Kotlin classes can just be used without any extras. Some Kotlin features are not present in de Java language, but I will show you how to use those features from Java.

Setting up your project

I use Maven in my projects and Baeldung has a post on how to setup your project, so why copy it here? Read it here:

Create a Java and Kotlin Project with Maven | Baeldung on Kotlin
Learn how to configure a Java+Kotlin project with Maven.

And you clone my project on Github containing the examples in this post and unit tests to see them in action:

sanderv/kotlin-blog-examples
Contribute to sanderv/kotlin-blog-examples development by creating an account on GitHub.

I will use this Person class in my examples:

class Person(
        var name: String,
        var birthDate: LocalDate,
        val userID: String
) {
    fun age(refDate: LocalDate = LocalDate.now()) = 
            Period.between(birthDate, refDate).years
}

Creating objects and interacting with them

Kotlin classes can be instantiated like a Java class. In Java, you can just write

Person kotlinPerson = new Person("Kotlin Person",
        LocalDate.of(2000, 1, 1),
        "kotlin");

Basic interaction is like you'd expect in Java:

kotlinPerson.getName();
kotlinPerson.setName("Sander");
kotlinPerson.getUserID();
kotlinPerson.setUserID(); // gives a compiler error, no setter because of 'val'

From the outside you can't tell if this is a Kotlin of Java object. Exactly why I think Kotlin is a great replacement for Project Lombok (see Kotlin, the essence of Java).

Default arguments

The age(...) function in the Person class has a default argument. But if you have this class up in your IDE, you will see that code completion will only show an age(LocalDate) function. This is because Java doesn't understand default arguments, just arguments.

The way Kotlin offers this in a usable way in Java is with the @JvmOverlads annotation:

@JvmOverloads
fun age(refDate: LocalDate = LocalDate.now()) =
        Period.between(birthDate, refDate).years

Now, if you use code completion on the object, you will see two overloaded functions, but without actually writing them.

Default arguments in constructors

Let's say you want the userID to be generated if not supplied to the constructor. Because userID is a parameter to the constructor, we can add a default argument (in this case a random UUID):

class Person(
        var name: String,
        var birthDate: LocalDate,
        val userID: String = UUID.randomUUID().toString()
)

To use it from Java, you would probably expect a @JvmOverloads annotation on the line above, same as with the age(...) function in the previous example. Unfortunately that would annotate the class, not the constructor.

Kotlin's constructor in this example is implicit. To annotate it, we have to make it explicit using the constructor keyword and then add the annotation to it:

class Person @JvmOverloads constructor(
        var name: String,
        var birthDate: LocalDate,
        val userID: String = UUID.randomUUID().toString()
)

Now we can create a Person from Java without supplying the userID.

Next steps

This is the last part in the "Kotlin, the essence of Java" series. You now have enough examples to start using Kotlin in your project.

Of course there's much more to Kotlin than I've shown so far, so take a look at the topics to broaden your Kotlin knowledge:

Tags