skip to content

Calling Kotlin From Java

The other direction: how your Kotlin declarations appear to a Java caller, and the annotations you add so they appear sensibly. Library authors are expected to know this cold.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

page 1 of 2

You have a Kotlin class with a companion object containing a function create(). How do you call that function from Java, and why isn't the call simply Foo.create()?

level: juniorimportance: must knowfreq 70%

answer

  1. Default: Foo.Companion.create()
  2. Companion = static field + nested class
  3. @JvmStatic adds a real static method
  4. Foo.create() fails without @JvmStatic
  5. Named companion renames Companion

basics

~10 s

From Java you call Foo.Companion.create(). Kotlin puts companion members on a nested object named Companion, so Java must go through that object rather than calling the method straight on the class.

solid answer

~40 s

A Kotlin companion object compiles to a real nested class/instance exposed to Java as a static field named Companion on the outer class. So `companion object { fun create() = ... }` is reached from Java as `Foo.Companion.create()`. Java does NOT see `Foo.create()` by default, because Kotlin's companion is an object instance, not a set of Java static methods. To make `Foo.create()` work, annotate the companion function with `@JvmStatic`, which generates a genuine static method on `Foo` (the Companion delegate stays too). For properties, `@JvmStatic` on the val/getter or `@JvmField`/`const` changes the surfaced shape. Without those annotations, the Companion indirection is mandatory from Java.

code

kotlin · 10 lines
kotlin
class Foo {
    companion object {
        @JvmStatic fun create(): Foo = Foo()
        fun build(): Foo = Foo()
    }
}
// Java:
// Foo.create();           // OK (@JvmStatic)
// Foo.Companion.build();  // OK (default path)
// Foo.build();            // does NOT compile

go deeper

for a junior

Knows the default Java path is Foo.Companion.create() and that @JvmStatic enables Foo.create().

for a middle

Explains the generated Foo$Companion class + static Companion field and that @JvmStatic adds a delegate without removing the field.

for a senior

Discusses API-design implications: annotate factory/constants for clean Java APIs, and the named-companion option for clarity.

for a principal

Sets a team convention for which companion members get @JvmStatic to keep a stable, idiomatic Java surface across a multi-language codebase.

## What a companion object is In Kotlin a class can have ONE `companion object` — a singleton tied to the class, used for factory functions, constants, and other 'class-level' members. Kotlin lets you write `Foo.create()` from Kotlin because the compiler resolves the companion implicitly. ## How it looks to Java The companion compiles to a real nested type. The JVM sees: - a nested class `Foo$Companion` - a `public static final Foo$Companion Companion` field on `Foo` So from Java the default access path is: ```java Foo.Companion.create(); ``` Java can't write `Foo.create()` because there is no static `create` method on `Foo` — the method lives on the `Companion` instance. ## Making it a true static: @JvmStatic Annotating the companion function makes the compiler ALSO emit a static method on the outer class: ```kotlin class Foo { companion object { @JvmStatic fun create(): Foo = Foo() fun build(): Foo = Foo() // no annotation } } ``` From Java: ```java Foo.create(); // works — @JvmStatic Foo.Companion.create(); // also still works Foo.Companion.build(); // only path for build() Foo.build(); // COMPILE ERROR ``` ## Key terms - **companion object**: the per-class singleton holding class-level members. - **`@JvmStatic`**: annotation that tells the Kotlin compiler to additionally generate a JVM `static` method/accessor, so Java can call it without the `Companion` hop. - **`Companion`**: the auto-generated name of the static field/nested class (you can rename it: `companion object Named { }` → `Foo.Named`). From Kotlin nothing changes — `Foo.create()` works either way. The annotations only affect the Java-facing surface.

  • Can you change the 'Companion' name Java sees?
    Yes — give the companion a name: `companion object Factory { }` makes Java write `Foo.Factory.create()`.
  • Does @JvmStatic remove the Companion field?
    No. The Companion field/instance still exists; @JvmStatic adds a static delegate in addition, so both paths compile.

The companion is like a receptionist named 'Companion' standing at the company's front desk — Java must ask the receptionist; @JvmStatic gives the company its own direct phone line.

saying these in an interview costs you the question

  • Claiming Java can call Foo.create() with no annotation
  • Thinking companion members are Java statics by default
  • Confusing companion object with a Java static nested class with static methods
  • Saying @JvmStatic is required for Kotlin callers too

context

open as a page

A Kotlin file Utils.kt declares a top-level function fun greet(): String. How does Java call it, and where does that method actually live on the JVM?

level: juniorimportance: must knowfreq 65%

basics

~10 s

Kotlin puts top-level functions into an auto-generated class named after the file plus 'Kt'. So Utils.kt's greet() is called from Java as UtilsKt.greet().

open as a page

What does the @JvmField annotation do, and why would you use it when calling Kotlin code from Java?

level: juniorimportance: must knowfreq 55%

basics

~10 s

@JvmField turns a Kotlin property into a plain public field. Java code can then read or write it directly as obj.x instead of calling getX() or setX().

open as a page

What does the @JvmName annotation do, and why would you put it on a Kotlin function or property accessor that Java code will call?

level: juniorimportance: must knowfreq 55%

basics

~20 s

@JvmName changes the name that a Kotlin function, getter, or setter shows up as when called from Java. It lets you give Java a nicer name or avoid a name conflict, without changing the Kotlin name.

open as a page

What does the @JvmOverloads annotation do, and why is it needed for Java callers?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Java cannot use Kotlin's default argument values. @JvmOverloads tells the compiler to also generate extra Java-visible overloads, one per default-parameter combination, so Java code can call the function while leaving some arguments out.

open as a page

What does the @JvmStatic annotation do, and why would a Kotlin author add it to a companion object function?

level: juniorimportance: must knowfreq 70%

basics

~10 s

@JvmStatic tells the Kotlin compiler to also generate a real static method. Then Java code can call Foo.bar() directly instead of the longer Foo.Companion.bar().

open as a page

Kotlin has no checked exceptions. What does the @Throws annotation do, and why would you put it on a Kotlin function?

level: juniorimportance: must knowfreq 55%

basics

~20 s

Kotlin never forces you to declare or catch exceptions. @Throws tells the compiler to add a Java 'throws' clause to the generated method, so Java code calling that Kotlin function can catch or declare the exception normally.

open as a page

Show how two Kotlin functions can produce a JVM signature clash due to type erasure, and how @JvmName resolves it.

level: middleimportance: must knowfreq 48%

basics

~20 s

Generics disappear at runtime, so List<String> and List<Int> both become plain List on the JVM. Two functions that differ only by that generic part get the same JVM signature and won't compile. Adding @JvmName to one gives it a different bytecode name, so both compile.

open as a page

Given fun box(w: Int, h: Int = 1, color: String = "red", filled: Boolean = true) annotated with @JvmOverloads, which exact method signatures become visible to Java?

level: middleimportance: must knowfreq 55%

basics

~10 s

Java sees one method per trailing-defaults combination: box(w), box(w,h), box(w,h,color), and box(w,h,color,filled). Three parameters have defaults, so four overloads total, each dropping defaults from the right.

open as a page

If you annotate a Kotlin function with @Throws, does it force other Kotlin code to catch the exception? Explain what changes and what does not.

level: middleimportance: must knowfreq 45%

basics

~10 s

No. @Throws never forces Kotlin callers to do anything — Kotlin has no checked exceptions. It only changes the generated JVM method signature so Java callers can catch or declare the exception.

open as a page

Contrast how a companion `const val MAX_RETRIES = 3`, a plain companion `val name = "x"`, and a companion `@JvmField val cache = ...` each surface to Java.

level: middleimportance: should knowfreq 55%

basics

~10 s

const val becomes a public static final you read directly (Foo.MAX_RETRIES). A plain val needs Foo.Companion.getName(). @JvmField exposes the field directly as Foo.cache, skipping the getter.

open as a page

Explain exactly what @JvmStatic does when placed on a companion member, what it does NOT change, and how a named companion object affects the Java access path.

level: middleimportance: should knowfreq 50%

basics

~20 s

@JvmStatic makes Kotlin emit an extra real static method on the outer class, so Java skips Companion. It doesn't remove the Companion object. Naming the companion changes 'Companion' to that name in the access path.

open as a page

How do you use @JvmField on companion object properties, and what does it fix for Java callers?

level: middleimportance: should knowfreq 40%

basics

~20 s

A companion property is normally reached from Java via the Companion holder and a getter. @JvmField on it moves the field up to the outer class as a static field, so Java writes Outer.NAME directly.

open as a page

Compare @JvmField with const for exposing values to Java. When does each apply, and what does Java see?

level: middleimportance: should knowfreq 45%

basics

~20 s

const makes a compile-time constant Java sees as a static final field. @JvmField exposes a normal mutable-or-not field with no accessors. You cannot combine them — const already produces a public static final field for Java.

open as a page

What is the FooKt facade class for top-level Kotlin declarations, and how does @file:JvmName change it?

level: middleimportance: should knowfreq 42%

basics

~20 s

Top-level Kotlin functions and properties in a file get wrapped in an auto-generated class named after the file plus 'Kt' (e.g. Utils.kt becomes UtilsKt). @file:JvmName renames that wrapper class so Java calls it by a nicer name.

open as a page

How do you apply @JvmOverloads to a primary constructor, and why is this a common pattern for Android custom Views?

level: middleimportance: should knowfreq 45%

basics

~10 s

Put @JvmOverloads on the primary constructor, after the visibility/constructor keyword: class MyView @JvmOverloads constructor(...). It generates the multiple constructors Android's layout-inflation and Java code expect, so you don't hand-write three or four constructors.

open as a page

Exactly what bytecode does the compiler emit for a @JvmStatic companion function, and where does the actual implementation live?

level: middleimportance: should knowfreq 50%

basics

~10 s

The compiler keeps the method on the Companion object and adds a second static method on the outer class. That static method just forwards the call to the Companion singleton's instance method.

open as a page

How does @JvmStatic behave on a companion-object property, and how does that differ from @JvmField and const?

level: middleimportance: should knowfreq 45%

basics

~10 s

@JvmStatic on a property creates static getter/setter methods on the outer class. @JvmField instead exposes the raw field directly. const makes a compile-time constant static final field. They solve different interop needs.

open as a page

Several Kotlin stdlib functions (like readText or use) already carry @Throws. Why, and what is the common pitfall when a Kotlin function delegates to Java code that throws a checked exception without re-declaring it?

level: middleimportance: should knowfreq 28%

basics

~20 s

Stdlib functions that wrap Java I/O carry @Throws so Java callers can handle the IOException Java would normally force them to. The pitfall: your own Kotlin wrapper around Java I/O won't declare anything unless you add @Throws yourself, so Java callers can't catch it.

open as a page

Which property declarations cannot be marked @JvmField, and why does each restriction exist?

level: seniorimportance: should knowfreq 35%

basics

~20 s

@JvmField needs a real backing field with default accessors. It's rejected on properties that are private, open/override, const, delegated, have custom get/set, are computed, or live in an interface — because there's no plain public field to expose.

open as a page

How do you rename a Kotlin property's getter or setter for Java callers using @JvmName, and what subtleties come with property accessors?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Properties compile to getX()/setX() methods. You can use @get:JvmName and @set:JvmName to rename those generated methods for Java, while Kotlin still accesses the property normally.

open as a page

What are the limitations and common pitfalls of @JvmOverloads — when does it not apply or cause clashes?

level: seniorimportance: should knowfreq 35%

basics

~20 s

It only helps with trailing defaults dropped right-to-left, needs at least one defaulted parameter to do anything, doesn't work on abstract/interface methods, and can clash with hand-written overloads. It also enlarges your public API surface.

open as a page

Where is @JvmStatic NOT allowed, and what subtle issue arises with @JvmStatic on a companion inside an interface?

level: seniorimportance: should knowfreq 30%

basics

~10 s

You can only use @JvmStatic on companion or named-object members. It is rejected on top-level functions, regular class members, and const vals. On interface companions it works but historically needed a newer JVM target.

open as a page

You want a suspend function and a SAM interface implementation to declare a checked exception to Java callers. Where exactly do you place @Throws, and what are the gotchas?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Put @Throws directly on the function or property accessor whose generated method should carry the throws clause. For suspend functions you can annotate them, but the clause lands on the suspending bytecode method; lambdas can't be annotated, so use an explicit function or object.

open as a page

Your companion object implements a Java interface (e.g. companion object : Comparator<Foo>). How does Java pass that companion instance to APIs expecting the interface, and how does that differ from calling a @JvmStatic factory?

level: seniorimportance: nice to knowfreq 30%

basics

~10 s

Java grabs the singleton via Foo.Companion and passes it where the interface is expected, e.g. Collections.sort(list, Foo.Companion). A @JvmStatic factory is a static method you invoke (Foo.create()), not an instance you hand off.

open as a page

Walk through the exact Java-visible members generated for a companion containing a private const val, a public const val, and a @JvmStatic var. What surprises Java callers here?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

A private const is invisible to Java. A public const becomes a static final field read directly (Foo.X). A @JvmStatic var generates static getX()/setX() on the class. The surprise: const is inlined, so changing it can leave stale values in compiled Java.

open as a page

You maintain a Kotlin library consumed heavily from Java. What are the design tradeoffs of exposing data with @JvmField instead of normal properties?

level: seniorimportance: nice to knowfreq 25%

basics

~10 s

@JvmField gives Java a clean direct field and slightly cheaper access, but you lose encapsulation: you can't later add validation, laziness, or override behavior without breaking the public API for already-compiled Java callers.

open as a page

From a library-design standpoint, what binary-compatibility and tooling risks does @JvmName introduce, and when would you avoid it?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Because @JvmName sets the actual compiled method name, that name becomes part of your public binary contract. Changing or removing it breaks Java callers and reflection. It can also confuse tools that expect standard naming.

open as a page

When designing a Kotlin library API consumed by Java, how do you decide between @JvmOverloads, explicit overloads, and a builder — and what binary-compatibility tradeoffs matter?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Use @JvmOverloads for simple trailing-optional parameters where Java just wants to omit the last few. Use explicit overloads when you need non-trailing combinations or different bodies. Use a builder when there are many independent optional fields. Remember each generated overload becomes part of your binary API.

open as a page

You own a Kotlin library heavily consumed by Java. How would you decide a consistent policy for @JvmStatic (and the related interop annotations) across factory/entry-point APIs?

level: principalimportance: nice to knowfreq 18%

basics

~10 s

Decide deliberately, not case by case. Apply @JvmStatic to all public companion/object entry points so Java gets clean Foo.create() calls, document it, and combine with @JvmName/@Throws/@JvmOverloads for an idiomatic Java surface.

open as a page

showing 1–30 of 31