Careeroza
Calling Kotlin from Java
Annotations for Java Callers
| Annotation | Effect for Java code |
|---|---|
@JvmStatic | a companion/object function becomes a real static method: Config.create() instead of Config.Companion.create() |
@JvmOverloads | generates overloads for parameters with default values |
@JvmField | exposes a property as a public field (no getter) |
const val | compile-time constant, a static final field |
@file:JvmName("Utils") | top-level functions in this file go to class Utils instead of FileNameKt |
@JvmName("...") | rename a function/getter in bytecode (solves signature clashes) |
@Throws(IOException::class) | adds throws so Java callers can catch a checked exception |
Checking the Bytecode with Reflection
Example
We can ask the JVM what Java code will see.
import java.lang.reflect.Modifier
class Config {
companion object {
@JvmStatic fun create() = Config()
fun plain() = Config()
const val VERSION = "2.1"
@JvmField val TAG = "cfg"
}
}
class Greeter @JvmOverloads constructor(
val name: String,
val greeting: String = "Hello",
val punctuation: String = "!",
)
fun main() {
val methods = Config::class.java.declaredMethods.map { it.name }
println("Config has static create(): ${"create" in methods && Modifier.isStatic(Config::class.java.getMethod("create").modifiers)}")
println("Config has plain() directly: ${"plain" in methods}")
val fields = Config::class.java.declaredFields.filter { Modifier.isStatic(it.modifiers) }.map { it.name }.sorted()
println("Static fields: $fields")
val ctorSizes = Greeter::class.java.constructors.filter { !it.isSynthetic }.map { it.parameterCount }.sorted()
println("Greeter constructors for Java: $ctorSizes parameters")
}Output
Config has static create(): true Config has plain() directly: false Static fields: [Companion, TAG, VERSION] Greeter constructors for Java: [1, 2, 3] parameters
The Java Side
// Java code using the Kotlin classes above
Config c = Config.create(); // works thanks to @JvmStatic
Config d = Config.Companion.plain(); // without it, go through Companion
String v = Config.VERSION; // const val
String t = Config.TAG; // @JvmField
Greeter g1 = new Greeter("Asha"); // @JvmOverloads
Greeter g2 = new Greeter("Asha", "Namaste");
Greeter g3 = new Greeter("Asha", "Namaste", "!!");
// Kotlin: @Throws(IOException::class) fun load(path: String): String
try {
String text = FileLoader.load("data.txt");
} catch (IOException e) {
e.printStackTrace();
}