skip to content

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

level: juniorimportance: must knowfreq 70%

answer

  1. No static in Kotlin → companion object is a real singleton
  2. Default: Java must write Foo.Companion.bar()
  3. @JvmStatic emits a real static method on outer class
  4. Java then writes Foo.bar()
  5. Interop-only; Kotlin call sites unchanged

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().

solid answer

~40 s

Kotlin has no static members; their replacement is the companion object or a named object, which is a real singleton instance. By default a companion function compiles to an instance method on the Companion singleton, so Java must write Foo.Companion.bar(). Adding @JvmStatic makes the compiler ALSO emit a genuine static method bar() on the outer class Foo, so Java can call Foo.bar(). It is purely an interop convenience: it changes the generated JVM bytecode (adds a static method/field) but does not affect Kotlin call sites, which already use Foo.bar(). It applies to companion-object and named-object members (functions and properties). Without it, Java interop with Kotlin 'static-like' members is verbose and surprising.

code

kotlin · 6 lines
kotlin
class MathUtil {
    companion object {
        @JvmStatic fun square(x: Int) = x * x  // Java: MathUtil.square(3)
        fun cube(x: Int) = x * x * x           // Java: MathUtil.Companion.cube(3)
    }
}

go deeper

for a junior

Knows it lets Java call Foo.bar() instead of Foo.Companion.bar() and that Kotlin has no static.

for a middle

Explains the generated Companion singleton and that the annotation is additive bytecode for Java interop.

for a senior

Distinguishes the default instance-method-on-Companion emission from the added static, covers properties and named objects.

for a principal

Frames it within an interop API-design policy and weighs it against alternatives like top-level functions or @JvmField.

## The problem: Kotlin has no `static` Kotlin removed the `static` keyword. Where Java uses static members, Kotlin uses a **companion object** (one per class, declared with `companion object { ... }`) or a **named object** (`object Foo { ... }`, a singleton). Both are real objects — instances — not static containers. ## What the compiler emits by default Given: ```kotlin class Foo { companion object { fun bar() = 42 } } ``` The compiler creates a nested class `Foo$Companion` and a single static field `Foo.Companion` holding that singleton. `bar()` becomes an **instance** method on `Foo$Companion`. From Kotlin you write `Foo.bar()` and the compiler routes it through the singleton automatically. From **Java**, though, there is no such sugar, so you must write the verbose, surprising: ```java Foo.Companion.bar(); ``` ## What `@JvmStatic` changes Annotating the member with `@JvmStatic`: ```kotlin class Foo { companion object { @JvmStatic fun bar() = 42 } } ``` tells the compiler to **also** emit a real `static` method `bar()` directly on the **outer class** `Foo`. Now Java can call: ```java Foo.bar(); // clean — calls the generated static method ``` The `Foo.Companion.bar()` form still exists too; `@JvmStatic` is purely additive at the bytecode level. ## Key facts - It affects **only generated JVM bytecode for Java callers**. Kotlin call sites already used `Foo.bar()` and are unchanged. - It works on members of a **companion object** or a **named `object`**. - It applies to **functions and properties** (for a property it generates a static getter/setter). - It is one of several JVM interop annotations alongside `@JvmField`, `@JvmName`, `@JvmOverloads`, and `@Throws`.

  • Does @JvmStatic change how Kotlin code calls the function?
    No. Kotlin already calls Foo.bar() regardless; @JvmStatic only adds a static method in the bytecode for Java's benefit.
  • Can you put @JvmStatic on a top-level function?
    No. Top-level functions already compile to static methods on a file class, so @JvmStatic is neither needed nor allowed there.

It's like adding a direct front-door shortcut so visitors (Java) don't have to walk around to the side entrance (Companion) every time.

saying these in an interview costs you the question

  • Claiming Kotlin has a static keyword
  • Thinking @JvmStatic changes Kotlin call syntax
  • Saying the Companion form stops working after adding it
  • Confusing it with @JvmField (which is about fields, not methods)
  • Believing it makes the function faster for Kotlin callers

context