Kotlin Calling Java

Particularly for new projects, most likely you will be focused on Kotlin code calling into Java code. While Kotlin libraries are growing in number, they pale in comparison to the vast array of Java libraries. In particular, many major frameworks are implemented in Java, such as the Android SDK and Spring. While they may get some Kotlin wrappers, many developers will wind up working with them directly.

On the whole, things tend to work fairly smoothly. There are a variety of occasions where you will need to think about interoperability, but for the most part, “it just works”. This is one of the reasons why Google was comfortable in endorsing Kotlin — if having Kotlin code call into the Android SDK was going to be some huge problem, Google might have been more cautious.

In this section, we will explore some things that you will need to consider as you have your Kotlin code call out to Java classes and methods.

Java Methods and Property Syntax

If you spend time on a Kotlin/JVM project in an IDE that offers Kotlin auto-complete (e.g., Android Studio, IntelliJ IDEA), you will rapidly discover that Kotlin allows you to invoke certain Java methods as if they were Kotlin properties or other simpler forms of Kotlin syntax.

getXXX() and setXXX() Pairs

The so-called “JavaBeans” method structure in Java involves paired methods, prefixed with get and set(), with a common base and using a common type:

public class Something {
  private String foo;

  public String getFoo() {
    return foo;
  }

  public void setFoo(String newFoo) {
    foo = newFoo;
  }
}

Here, we have JavaBeans-style accessor methods for a foo field, with getFoo() returning the foo value and setFoo() changing it.

If you access this Java code from Kotlin, you can treat the getFoo() and setFoo() methods as if you were accessing a foo property that was public:

class Bar {
  fun doFoo() {
    val thingy = Something()
    val oldFoo = thingy.foo
    
    thingy.foo = "this is the replacement"
  }
}

There is nothing stopping you from calling the Java methods directly, so this works too:

class Bar {
  fun doFoo() {
    val thingy = Something()
    val oldFoo = thingy.getFoo()

    thingy.setFoo("this is the replacement")
  }
}

However, your IDE might hint to you to switch to “property access syntax”:

Android Studio, Suggesting Property Access Syntax
Android Studio, Suggesting Property Access Syntax

So, while calling the methods directly works, your IDE would prefer that you use the shorter syntax.

Note that property access syntax only works when there is a matching getter and setter with the same base name and operating on the same type. For example, you might have a Java class with a single getter but overloaded setters, such as with Android’s TextView and its text “property”. There are several setText() variants, accepting different parameters. If there is a matching pair — such as the CharSequence versions of getText() and setText() on TextView — property access syntax works:

class Goo {
  fun doText(tv: TextView) {
    val oldText = tv.text

    tv.text = "This is new!"
  }
}

However, other setters that work with other types require you to call the setter method directly, not use property assignment syntax:

class Goo {
  fun doText(tv: TextView) {
    val oldText = tv.text

    tv.setText(R.string.app_name)
  }
}

If you try tv.text = R.string.app_name, you get a type mismatch error, as the property assignment is expecting a CharSequence, and R.string.app_name (in the world of Android) is an Int.

The isXXX() Variant

Sometimes, with boolean or Boolean values, the Java getter method uses is...() instead of get...() as a naming convention:

public class Something {
  private boolean foo;

  public boolean isFoo() {
    return foo;
  }

  public void setFoo(boolean newFoo) {
    foo = newFoo;
  }
}

This too maps to a property in Kotlin, though the property name will be the same as the getter method name — isFoo in this case:

class Bar {
  fun doFoo() {
    val thingy = Something()
    val oldFoo = thingy.isFoo

    thingy.isFoo = !oldFoo
  }
}

Technically, this works for any data type, not just Boolean. In practice, though, you will rarely see Java code use is...() for a getter that does not return a boolean or Boolean.

get() as Square Brackets

Kotlin uses square bracket notation for accessing the contents of things like a List or Map. In the case of a List, the value in the brackets is the 0-based index, while for a Map, the value in the brackets is the Map key.

Kotlin also extends that notation to any Java class that has appropriate method signatures.

So, if the Java class has get() and set() methods that work off of an int or Integer index:

public class Something {
  private ArrayList<String> stuff = new ArrayList<>(100);

  public String get(int i) {
    return stuff.get(i);
  }

  public void set(int i, String value) {
    stuff.set(i, value);
  }
}

…then Kotlin allows square-bracket notation:

class Bar {
  fun doFoo() {
    val thingy = Something()

    thingy[0] = "hello"
    thingy[1] = "world"

    println("${thingy[0], ${thingy[1]}")
  }
}

In fact, the get() and set() methods can use any type for their “key”, not just int:

public class Something {
  private HashMap<String, String> stuff = new HashMap<>();

  public String get(String key) {
    return stuff.get(key);
  }

  public void set(String key, String value) {
    stuff.put(key, value);
  }
}
class Bar {
  fun doFoo() {
    val thingy = Something()

    thingy["first key"] = "hello"
    thingy["second key"] = "world"

    println("${thingy["first key"]}, ${thingy["second key"]}")
  }
}

It is relatively unlikely that you will have a Java class that conforms to this system, but if it does, you are welcome to use this Kotlin notation to work with it.

Dealing with null

Kotlin has a deep relationship with null:

Most languages do not treat null with the same reverence. In particular, Java does not.

This causes some problems when Kotlin needs to work with Java objects. For example, suppose that we have:

public class JavaStuff {
  public String getSomething() {
    return null;
  }
}

…and we try using this from Kotlin:

val thingy = JavaStuff().something

println(thingy.length)

The Kotlin compiler will tend to assume that getSomething() is returning String, not String?, and so it will treat thingy as a String… and we will crash at runtime with a NullPointerException.

If the Java code has annotations that indicate nullability, though, Kotlin will attempt to use them.

import androidx.annotation.Nullable;

public class JavaStuff {
  public @Nullable String getSomething() {
    return null;
  }
}

Here, we use an Android SDK annotation, @Nullable, to indicate that the return value of getSomething() might be null. Kotlin, in turn, will then treat getSomething() as returning String? instead of String.

Many libraries, such as the Android SDK, use these annotations to help Java and Kotlin developers. However, not all libraries offer them. As a result, be careful when calling Java code from Kotlin, and consider introducing the nullable type yourself. For example, even in the earlier Java example without @Nullable, we could write our Kotlin as:

val thingy: String? = JavaStuff().something

println(thingy?.length)

Here, we are saying that we do not trust getSomething() to always return a non-null value, so we force thingy to be String? for safety’s sake.

Java Class Objects

Sometimes in Java, we need to refer to Java Class objects. In Android development, one prominent scenario for this is in creating explicit Intent objects:

startActivity(new Intent(this, TheOtherActivity.class));

In Java, .class as a suffix on the class name returns the Class object associated with that class, and we can pass that Class object to methods that need one.

In Kotlin, ::class as a suffix on the class name also returns an object representing the class. However, this is a KClass — a Kotlin class object. We need to use ::class.java to get at the Java Class object instead. So this will not compile:

startActivity(Intent(this, MainActivity::class))

…but this will:

startActivity(Intent(this, MainActivity::class.java))

There will be cases in Kotlin code where you just use ::class. That means the function that you are calling takes a KClass parameter, probably because that function was written in Kotlin and is designed for use with Kotlin classes.

The SAM Scenario

Every now and then, Java winds up being easier to use from Kotlin than is Kotlin itself.

For example, take this simple Java interface:

public interface SomeJavaInterface {
  void beUseful(String value);
}

Implementations need to override beUseful() and… well… be useful.

You can have the equivalent interface in Kotlin, of course:

interface SomeKotlinInterface {
  fun beUseful(value: String)
}

Other than the name, the interfaces seem identical.

However, using them from Kotlin is significantly different.

To use the Kotlin interface, we need to create an object that overrides beUseful():

val thingy = object : SomeKotlinInterface {
  override fun beUseful(value: String) {
    println(value)
  }
}

To use the Java interface, we could do the same basic thing:

val thingy = object : SomeJavaInterface {
  override fun beUseful(value: String) {
    println(value)
  }
}

In practice, though, for Java we can get away with just:

val thingy = SomeJavaInterface { println(it) }

SomeJavaInterface implements the SAM pattern: Single Abstract Method. Kotlin knows how to convert a lambda expression into the implementation of that single abstract method, which in turn allows for the succinct syntax that we see here.

But if you try using that syntax with SomeKotlinInterface, you fail with a compile error.

Historically, Kotlin did not support the use of SAM for Kotlin interfaces. The argument boils down to: you should not create SomeKotlinInterface. Instead, use a function type, possibly with a typealias:

typealias SomeKotlinInterface = (String) -> Unit

Now, you can get similar syntax as you get with SAM conversion:

val thingy: SomeKotlinInterface = { println(it) }

However, Kotlin 1.4 added functional interfaces which allow for SAM syntax for specially-constructed interfaces.

void and Unit

At this point, though, you may be wondering what Unit is.

All functions in Kotlin return something. If you do not specify otherwise, the function returns Unit. This is a singleton: there is exactly one Unit in the environment.

You do not see Unit mentioned that much because much of the time it can be safely dropped from Kotlin syntax. For example, this function:

fun ofMeasure() {
  println("hello, world!")
}

…is really:

fun ofMeasure(): Unit {
  println("hello, world!")
  
  return Unit
}

If your function body does not have an explicit return, it returns Unit. And, if your function declaration does not have a return type, it returns Unit. Kotlin just lets us skip the Unit references.

While we can skip Unit from an actual function declaration and the return type, a function type cannot skip it. We cannot replace:

typealias SomeKotlinInterface = (String) -> Unit

with:

typealias SomeKotlinInterface = (String) ->

or:

typealias SomeKotlinInterface = (String)

(the latter compiles but is no longer a function type)

Java methods that “return” void, when referenced in Kotlin, really return Unit.

Type Mapping

If you have a Java class named org.yourapp.Restaurant, and you create an instance of it in Kotlin, you get a org.yourapp.Restaurant object.

This may seem obvious.

However, not all types work that way. There is a long list of Java types that get “mapped” to Kotlin native types.

For example:

The Kotlin documentation has the full roster of mapped types.

Usually, we do not think about this much. However, it may raise some challenges when using reflection or similar techniques that inspect class details at runtime.

Keyword Differences

There is a fair amount of overlap in keywords between Kotlin and Java, such as class and return. However, there are some Kotlin keywords that are not Java keywords, such as object and when.

As a result, from time to time, you will see function names wrapped in backticks — this is to allow you to call, say, a Java when() function, despite the fact that when is a Kotlin keyword.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.