Kotlin Interoperability with Java
Kotlin and Java work side by side in the same project. You can call Java code from Kotlin and Kotlin code from Java without any special bridges. This makes it easy to adopt Kotlin gradually in existing Java projects or to use the vast Java ecosystem of libraries from Kotlin.
Calling Java from Kotlin
import java.util.Date
import java.util.ArrayList
import java.text.SimpleDateFormat
fun main() {
// Java standard library works directly in Kotlin
val date = Date()
val formatter = SimpleDateFormat("dd-MM-yyyy")
println(formatter.format(date))
// Java collections work in Kotlin
val javaList = ArrayList()
javaList.add("Kotlin")
javaList.add("Java")
javaList.forEach { println(it) } // Kotlin's forEach on Java list
} Platform Types
Java methods can return null — Kotlin cannot know if the return is nullable or not.
These are called "platform types" and are written as T! in error messages.
Java code:
public String getUserName() { return null; } // might return null!
Kotlin sees:
getUserName() returns String! (could be String or String?)
Best practice: treat Java return values as nullable
val name: String? = javaObject.getUserName()
val name = javaObject.getUserName() ?: "Default"@JvmStatic and @JvmField
// Kotlin companion object → Java static member
class Config {
companion object {
@JvmStatic
fun getVersion(): String = "1.0.0"
@JvmField
val MAX_SIZE = 100
}
}
// Java can call:
// Config.getVersion() ← works because of @JvmStatic
// Config.MAX_SIZE ← works because of @JvmField
// Without @JvmStatic:
// Config.Companion.getVersion() ← verbose in Java
@JvmOverloads — Kotlin Default Args in Java
// Kotlin function with default arguments
// Java does NOT support default arguments
class Greeter {
@JvmOverloads
fun greet(name: String = "Guest", greeting: String = "Hello") {
println("$greeting, $name!")
}
}
// Without @JvmOverloads — Java must always pass all arguments:
// new Greeter().greet("Alice", "Hi")
// With @JvmOverloads — Java gets overloaded versions:
// new Greeter().greet() → Hello, Guest!
// new Greeter().greet("Alice") → Hello, Alice!
// new Greeter().greet("Alice", "Hi") → Hi, Alice!
@JvmName — Control Generated Names
// Kotlin extension functions can conflict with Java method names
@JvmName("isValidEmailAddress")
fun String.isValidEmail(): Boolean = contains("@") && contains(".")
// Java calls it as:
// StringExtensionsKt.isValidEmailAddress(email)
// File-level @JvmName renames the generated class:
@file:JvmName("StringUtils")
package com.myapp.utils
// Java calls functions in this file as:
// StringUtils.someFunction()Calling Kotlin from Java
// Kotlin file: Calculator.kt
package com.myapp
class Calculator {
fun add(a: Int, b: Int) = a + b
companion object {
@JvmStatic
fun pi() = 3.14159
}
}
// Top-level function in MathUtils.kt
fun square(n: Int) = n * n
// Java:
// Calculator calc = new Calculator();
// int result = calc.add(5, 3);
// double pi = Calculator.pi();
// int sq = MathUtilsKt.square(4); // top-level functions go in FileName + "Kt"
Null Safety at the Boundary
// Annotate Java return types to help Kotlin understand nullability:
// @Nullable → Kotlin sees String?
// @NonNull → Kotlin sees String
// In Java:
// @Nullable
// public String findUser(int id) { ... }
// In Kotlin:
// val user: String? = javaService.findUser(42) // correctly nullable
// Without annotation, Kotlin sees String! (platform type)
// — it compiles but may crash if null at runtimeUsing Java Libraries in Kotlin
import com.google.gson.Gson
import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
fun main() {
// Gson (Java library) in Kotlin
val gson = Gson()
val json = gson.toJson(mapOf("name" to "Alice", "age" to 30))
println(json) // {"name":"Alice","age":30}
// OkHttp (Java library) in Kotlin — works perfectly
val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.build()
println("OkHttpClient ready: $client")
}Practical Interop: Migrating a Java Class
// Original Java class:
// public class OrderService {
// private String dbUrl;
// public OrderService(String dbUrl) { this.dbUrl = dbUrl; }
// public List getOrders(int userId) { ... }
// }
// Kotlin class calling the Java service:
class OrderViewModel(private val service: OrderService) {
fun loadOrders(userId: Int): List {
return service.getOrders(userId) ?: emptyList() // handle platform type
}
}
// Kotlin replacement for the Java class:
class OrderServiceKt(private val dbUrl: String) {
fun getOrders(userId: Int): List {
// Kotlin implementation
return listOf("Order #1", "Order #2")
}
} 