Lesson 21 / 25

Java Interoperability

Platform types, annotations and calling in both directions.

Two languages, one codebase

Kotlin calls Java directly: getters and setters appear as properties, and SAM interfaces accept lambdas. Values from Java have platform types (shown as String! in the IDE): the compiler does not know their nullability, so assign them to an explicitly nullable or non-null type at the boundary. Nullability annotations in Java, such as JetBrains @Nullable/@NotNull or JSpecify annotations, let Kotlin see real types. For Java callers of Kotlin code, annotations help: @JvmStatic exposes companion functions as static methods, @JvmOverloads generates overloads for default arguments, @JvmField exposes a property as a field, @JvmName renames, and @Throws declares checked exceptions for Java.

Making Kotlin friendly for Java callers

Annotations at the boundary.

import java.io.IOException

class Temperature private constructor(val celsius: Double) {
    companion object {
        @JvmStatic                     // Java: Temperature.ofCelsius(21.0)
        fun ofCelsius(value: Double) = Temperature(value)
    }
}

class HttpClientConfig @JvmOverloads constructor(   // Java gets several constructors
    val baseUrl: String,
    val timeoutMs: Int = 5_000,
    val retries: Int = 3,
)

@Throws(IOException::class)            // visible to Java as a checked exception
fun readConfig(path: String): String = java.io.File(path).readText()

fun javaBoundary() {
    val home: String? = System.getProperty("user.home")   // platform type made explicit
    println(home ?: "unknown")
}

Pin down platform types early

Declare the Kotlin type explicitly when you receive a value from Java. Otherwise a null can travel far into Kotlin code before failing with a confusing NullPointerException.

Quick check: What does `@JvmStatic` on a companion object function do?

  • Makes it run on a background thread
  • Makes it callable from Java as a static method on the class
  • Prevents Java from calling it
  • Marks it as thread-safe
Answer

Makes it callable from Java as a static method on the class — Without it, Java must call it via the Companion instance.