thecodex.expert · The Codex Family of Knowledge
Kotlin

Kotlin and Java Interop

Kotlin’s celebrated null safety has one genuine, honest gap — anything returned from Java code is a platform type, and Kotlin has no way to verify whether it can actually be null.

Kotlin 2.1 Platform types: a real null-safety gap Last verified:
Canonical Definition

Kotlin and Java compile to the same JVM bytecode, so calling Java methods and classes from Kotlin (and vice versa) works directly, with no wrapper layer needed. The genuine complication is nullability: Java has no built-in concept of nullable vs. non-null types (absent explicit @Nullable/@NonNull annotations), so a value returned from Java code is treated in Kotlin as a platform type — Kotlin simply doesn't know if it can be null, and suspends its usual compile-time null-checking for that value, trusting the programmer to handle it correctly. @JvmStatic exposes a companion object member as a genuine static method callable from Java; @JvmOverloads generates the multiple overloaded methods Java needs to approximate Kotlin's default-parameter functions.

Platform types: the real gap in null safety

String! means Kotlin genuinely doesn't know if getProperty can return null — it compiles as EITHER a nullable or non-null String, entirely without a compile-time safety net, until the actual runtime behavior of the Java method is understood and handled deliberately.

Kotlinplatform_types.kt
// calling a Java method: System.getProperty(String) returns java.lang.String
val value = System.getProperty("some.key")   // type shown by tooling as: String!  (platform type)

// Kotlin has NO compile-time guarantee here — value might genuinely be null
println(value.length)   // compiles fine — but could throw NullPointerException at runtime,
                          // exactly the crash Kotlin's own types normally prevent

@JvmStatic: exposing a real static method to Java

Without @JvmStatic, Java code would need to write Companion.create(...) to call it — the annotation makes it a genuine static method, callable the natural way Java code expects.

Kotlinjvmstatic.kt
class Config {
    companion object {
        @JvmStatic
        fun create(): Config = Config()
    }
}
// from Java: Config.create() — natural, thanks to @JvmStatic
// without it, Java would need: Config.Companion.create()

@JvmOverloads: default parameters, made visible to Java

Java has no concept of a default parameter value at all — @JvmOverloads generates the actual overloaded method variants Java code needs to call this function with fewer arguments.

Kotlinjvmoverloads.kt
class Greeter {
    @JvmOverloads
    fun greet(name: String, greeting: String = "Hello") {
        println("$greeting, $name!")
    }
}
// from Java, BOTH of these now exist and are callable:
// greeter.greet("Alice");
// greeter.greet("Alice", "Welcome");

Sources

1
JetBrains. Kotlin Documentation, "Calling Java from Kotlin" and "Calling Kotlin from Java," kotlinlang.org/docs/java-interop.html.