Что такое аннотация @JvmStatic и когда она нужна?

Аннотация @JvmStatic в Kotlin используется для улучшения совместимости Kotlin-кода с Java. Она сообщает компилятору Kotlin, что определённую функцию или свойство в object или companion object нужно сгенерировать как статический метод/поле в байткоде JVM. Это позволяет Java-коду обращаться к этим членам так, как если бы они были обычными static методами/полями в Java.

Где можно использовать @JvmStatic

Аннотацию @JvmStatic можно применять:

  • Внутри object (singleton-объекты).

  • Внутри companion object (статические секции классов).

  • Для функций.

  • Для свойств (только val и var с @JvmField, либо с get/set).

Поведение по умолчанию без @JvmStatic

В Kotlin любой метод, объявленный в object или companion object, на уровне JVM по умолчанию не является static, а реализуется как обычный метод экземпляра вложенного объекта:

class Utils {
companion object {
fun sayHello() = println("Hello")
}
}

Java не может вызвать этот метод напрямую как Utils.sayHello().

Java-код будет выглядеть так:

Utils.Companion.sayHello();

Что делает @JvmStatic

Если к той же функции добавить @JvmStatic, Kotlin создаёт два метода:

  • Один как static, доступный напрямую из Java.

  • Второй — обычный метод, как и раньше (для вызова из Kotlin).

class Utils {
companion object {
@JvmStatic
fun sayHello() = println("Hello")
}
}

Теперь Java может вызвать:

Utils.sayHello(); //  статический вызов
Utils.Companion.sayHello(); //  обычный вызов через объект

То же касается object:

object Logger {
@JvmStatic
fun log(msg: String) { println(msg) }
}

Java:

Logger.log("Hello"); // 
Logger.INSTANCE.log("Hello"); // тоже возможно, как без аннотации

Применение к свойствам

Можно использовать @JvmStatic с геттерами и сеттерами:

object Settings {
@JvmStatic
val version: String = "1.0"
}

Java:

String v = Settings.getVersion();

Чтобы получить прямой доступ к полю без геттера, используют @JvmField, а не @JvmStatic:

object Settings {
@JvmField
val version: String = "1.0"
}

Java:

String v = Settings.version;

Зачем вообще нужна @JvmStatic

Kotlin ориентирован на использование object и companion object, но в Java такой модели нет. Там есть только static. Когда Kotlin-код должен быть использован из Java (например, в Android, библиотеке или миграции), приходится адаптировать его интерфейс под Java-реалии.

Без @JvmStatic, вызов Java → Kotlin требует обращения к Companion, что непривычно и может быть неудобно.

Особенности компиляции

Когда вы используете @JvmStatic, компилятор Kotlin:

  • Генерирует дополнительный static-метод в том классе, где вы это указали.

  • Не удаляет старый метод — он остаётся для вызова из Kotlin.

  • Позволяет использовать оба подхода вызова в Java — и через Companion, и напрямую как static.

Пример полного класса

class MyMath {
companion object {
fun square(x: Int): Int = x \* x
@JvmStatic
fun cube(x: Int): Int = x \* x \* x
}
}

Java:

int a = MyMath.Companion.square(5); // через Companion
int b = MyMath.cube(5); // напрямую как static

Когда использовать @JvmStatic

  1. Если ваш Kotlin-код будет использоваться из Java.
    Это актуально в Android, библиотеках общего назначения или при миграции Java-проектов на Kotlin.

  2. Если вы хотите улучшить читаемость и привычность интерфейса для Java-разработчиков.
    Вместо ClassName.Companion.method() они смогут использовать ClassName.method().

  3. Когда используете Kotlin-объекты в Java-коде Android.
    Например, вызовы из Activity, Fragment, BroadcastReceiver и других Java-классов.

Когда не надо использовать @JvmStatic

  • Если вы пишете код исключительно на Kotlin и не планируете Java-интеграции.

  • Если не требуется обращаться к этим методам как к static в Java.

  • Если используете Kotlin Multiplatform — @JvmStatic специфична только для JVM.