Loslegen
Javaneer
Zurück zur Stufe
Stufe 0·Warum Kotlin, und Interop zuerst

Kotlin aus Java aufrufen

Die andere Richtung, die etwas Sorgfalt braucht: @JvmStatic, @JvmOverloads und @JvmName steuern, wie Kotlins Features (Default-Argumente, Companions, Top-Level-Funktionen) für Java erscheinen.

14 Min. LesezeitExperte

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

The reverse direction - calling Kotlin from Java - also works, but it's the direction that needs a little care. Kotlin has features Java doesn't (default arguments, top-level functions, companion objects, properties), and when Java calls Kotlin, those features have to be expressed in terms Java understands. A handful of @Jvm... annotations let you control exactly how your Kotlin appears to Java callers - essential when you're writing Kotlin that a Java codebase must consume.

The mismatch: Kotlin has things Java lacks

Java sees Kotlin as ordinary bytecode (last lesson), but some Kotlin conveniences translate awkwardly by default:

  • Default arguments - fun greet(name: String = "world") is one function in Kotlin, but Java has no default parameters, so by default Java can only call it with all arguments supplied.
  • Top-level functions - Java can't call a function outside a class, so Kotlin puts it on a FileNameKt class as a static method (which Java must reference by that generated name).
  • Companion objects - Kotlin's replacement for static members lives in a nested Companion object, so Java sees MyClass.Companion.create() rather than MyClass.create().
  • Properties - a Kotlin val/var is a getter/setter to Java, not a field.

These aren't broken - they just look clunky from Java unless you nudge them. That's what the annotations do.

The @Jvm annotations

Four annotations cover almost every "make this nice for Java" need:

  • @JvmStatic - on a companion-object member, generates a real static method so Java calls MyClass.create() instead of MyClass.Companion.create():
class InvoiceFactory {
    companion object {
        @JvmStatic fun create(id: String) = Invoice(id)   // Java: InvoiceFactory.create("A")
    }
}
  • @JvmOverloads - on a function with default arguments, generates overloaded methods for each combination, so Java can call it with fewer arguments:
@JvmOverloads
fun greet(name: String = "world", loud: Boolean = false) { ... }
// Java gets: greet(name, loud), greet(name), and greet()
  • @JvmName - renames the generated class or method Java sees. Useful to give a top-level-functions file a clean class name (@file:JvmName("Pricing") so Java calls Pricing.applyTax() not PricingKt.applyTax()), or to resolve a signature clash.
  • @JvmField - exposes a Kotlin property as a plain public field (no getter) when Java code expects direct field access.

Reach for these when - and only when - your Kotlin is meant to be consumed by Java.

Design for the consumer

The practical rule: if Kotlin code has Java callers, design its public API with them in mind. A pure-Kotlin module with no Java consumers needs none of these annotations - the interop machinery is invisible within Kotlin. But a Kotlin library or shared module used by Java should:

  • Add @JvmStatic/@JvmOverloads to factory methods and functions with defaults, so Java calls read naturally.
  • Use @JvmName/@file:JvmName to give top-level function files a clean class name.
  • Consider whether Java callers need the null-safety guarantees carried across (Kotlin's non-null types emit runtime null checks that protect Java callers too).

This mirrors the API-design principle from the Architecture path: your public surface is a contract, and here the consumer's language shapes what a good contract looks like.

Only annotate the Java-facing surface

Don't sprinkle @JvmStatic/@JvmOverloads everywhere reflexively - they add generated methods and only matter at the Kotlin→Java boundary. Apply them to the public API that Java actually calls, and leave internal Kotlin code clean. A quick check: if a Kotlin member has no Java callers, it needs no @Jvm annotations. When in doubt, write a small Java call site (or check the decompiled bytecode) to see whether the default interop is already ergonomic.

Writing labels in a second language for specific visitors

Pure Kotlin talking to Kotlin is a household speaking its native language - no translation needed. But when Java guests visit and need to use your kitchen (call your Kotlin API), some of your labels confuse them: your one jar marked 'greet (name optional)' means nothing to a guest who can only read fixed labels (no default arguments), and your communal 'statics shelf' is tucked inside a cupboard called 'Companion' they don't think to open. The @Jvm annotations are you adding clear second-language labels for the guests: @JvmOverloads puts out several pre-filled jars so a guest can grab 'greet()' or 'greet(name)'; @JvmStatic moves the shared items onto an obvious shelf labeled the way guests expect; @JvmName renames a cupboard sensibly. You only relabel the things guests actually touch - the rest of your kitchen stays in your own convenient language.

Make a Kotlin API Java-friendly

You wrote a Kotlin utility that a large Java codebase must use: a top-level function fun formatMoney(amount: Double, currency: String = "USD", symbol: Boolean = true): String in a file moneyUtils.kt, and a factory in a companion object: fun of(cents: Long): Money. Currently Java calls are ugly. Show which annotations make each Java-friendly and what the resulting Java calls look like.

Why do you sometimes need @JvmStatic, @JvmOverloads, or @JvmName when calling Kotlin from Java?

Key takeaways

  • Calling Kotlin from Java works but needs care, because Kotlin has features Java lacks: default arguments, top-level functions, companion objects, and properties.
  • @JvmStatic generates a real static from a companion member (Java: MyClass.create() not MyClass.Companion.create()).
  • @JvmOverloads generates overloaded methods for a function with default arguments, so Java can call it with fewer args.
  • @JvmName renames the generated class/method Java sees (e.g. give a top-level-functions file a clean class name); @JvmField exposes a property as a plain field.
  • Design the Java-facing surface with the consumer in mind - annotate only the API Java actually calls, and leave pure-Kotlin code clean.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten