1. Overview

In this tutorial, we’re going to discuss the interoperability between Java and Kotlin. We’re going to cover some basic examples as well as some other more complex scenarios.

2. Setting up Kotlin

Creating a Kotlin project is very simple using IntelliJ, Eclipse, and even the command line – however for this tutorial we’re going to follow the installation steps from our previous tutorial Introduction to Kotlin since it already has what we need for our demo purposes.

3. The Basics

Calling Java from Kotlin is straightforward and smooth since it was built with the idea of interoperability.

Let’s create this Customer class using core Java:

public class Customer {

    private String firstName;
    private String lastName;
    private int age;

    // standard setters and getters

}

4. Getters and Setters

Let’s now work with this simple Java POJO from Kotlin.

Getters and setters that follow the Java convention for these types of methods are represented as attributes in Kotlin:

val customer = Customer()

customer.firstName = "Frodo"
customer.lastName = "Baggins"

assertEquals(customer.firstName, "Frodo")
assertEquals(customer.lastName, "Baggins")

It’s worth noting that the new keyword is not required for instantiating an object.

The language tries to avoid boilerplate code as much as possible so we do not call getters/setters explicitly – we can simply use them using the field notation.

We need to remember that if a Java class has only setter methods, the property will not be accessible since the language does not support set-only properties.

If a method returns void then when it is called from Kotlin it will return Unit.

5. Null Safety

Kotlin is well known for its null safety feature, but as we know, this is not the case for Java, which makes it impractical for objects coming from it. A very simple example can be seen if we have a String array:

val characterList = ArrayList<String>()
characterList.add("Bilbo")
val character = list[0]

Kotlin doesn’t display any nullability errors at compile time when a method is being called on a variable of a platform type – and this type can’t be written explicitly in the language. So when a value is assigned, we can rely on this inference, or we can just choose the type we expect:

val a: String? = character
val a: String = character

They’re both allowed, but in the case of the non-null type, the compiler will immediately assert upon assignment, which will prevent the variable to hold a null value.

In the end, the compiler does its best to avoid nulls, but still, it’s impossible to eliminate it because of generics.

6. Arrays

In Kotlin arrays are invariant – which means that it wouldn’t allow us to assign Array<Int> to Array<Any> to prevent runtime failures.

So we have an example class:

public class ArrayExample {

    public int sumValues(int[] nums) {
        int res = 0;

        for (int x:nums) {
            res += x;
        }

        return res;
    }
}

If we want to pass an array of primitives to this method, we have to use one of the specialized classes from Kotlin:

val ex = ArrayExample()
val numArray = intArrayOf(1, 2, 3)

assertEquals(ex.sumValues(numArray), 6)

7. Varargs

Java gives us the capability of passing any number of arguments to a method:

public int sumArgValues(int... sums) {
    // same as above
}

The process is the same, with the slight difference that we need to use the spread operator * to pass the array:

assertEquals(ex.sumValues(*numArray), 6)

Currently, there’s no possibility of passing null to a varargs method.

8. Exceptions

In Kotlin all exceptions are unchecked, which means that the compiler will not force us to catch any exceptions:

// In our Java code

public void writeList() throws IOException {
    File file = new File("E://file.txt");
    FileReader fr = new FileReader(file);
    fr.close();
}

// In Kotlin

fun makeReadFile() {
    val ax = ArrayExample()
    ax.writeList()
}

9. Reflection

Simply put, reflection works on both Kotlin and Java classes:

val instance = Customer::class.java
val constructors = instance.constructors

assertEquals(constructors.size, 1)
assertEquals(constructors[0].name, "com.baeldung.java.Customer")

We can also obtain getter and setter methods, a KProperty for a Java field, and a KFunction for a constructor.

10. Object Methods

When objects are imported to Kotlin, all references of the type java.lang.Object get changed to kotlin.Any:

val instance = Customer::class
val supertypes = instance.supertypes

assertEquals(supertypes[0].toString(), "kotlin.Any")

11. Conclusion

This quick tutorial provides us a bigger understanding of Kotlin Java Interoperability. We had a look at some simple examples to show how Kotlin generally leads to less verbose code overall.

As always, the implementation of all of these examples and snippets can be found over on GitHub.

Comments are closed on this article!