skip to content

@JvmStatic

@JvmStatic emits a real static member so Java can call Foo.bar() instead of Foo.Companion.bar(). It is the smallest, most common courtesy you extend to Java callers of a Kotlin API.

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

questions

5

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

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

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 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