Avatar ·

Как перевести статические методы из Java в Kotlin? (эквивалент static)

Всем привет! Перехожу с Java на Kotlin, и столкнулся с проблемой. В Java я часто использую статические методы для утилитных классов, например, для работы с датами или валидации строк. Вот типичный Java-код: ```java public class StringUtils { public static boolean isEmpty(String str) { return str == null || str.trim().isEmpty(); } public static String capitalize(String str) { if (str == null || str.isEmpty()) return str; return str.substring(0, 1).toUpperCase() + str.substring(1); } } // Использование: if (StringUtils.isEmpty(userInput)) { System.out.println("Input is empty"); } ``` Пытаюсь перенести это на Kotlin, но ключевое слово `static` в Kotlin отсутствует. Пробовал просто писать `companion object`, но это выглядит как-то громоздко. Кроме того, я слышал про какие-то "объекты-компаньоны" и "функции верхнего уровня", но не до конца понимаю, чем они отличаются и когда что использовать. Подскажите, пожалуйста, как правильно организовать статические методы в Kotlin? Есть ли прямой эквивалент Java static? И чем отличаются разные подходы? Заранее спасибо! <<>>

Прямой эквивалент: companion object

В Kotlin нет ключевого слова static, но есть companion object (объект-компаньон). Это самый близкий аналог. Объявляется внутри класса, и его члены вызываются через имя класса, как статические методы в Java.

class StringUtils {
    companion object {
        fun isEmpty(str: String?): Boolean {
            return str == null || str.trim().isEmpty()
        }

        fun capitalize(str: String?): String? {
            if (str == null || str.isEmpty()) return str
            return str.substring(0, 1).uppercase() + str.substring(1)
        }
    }
}

// Использование:
if (StringUtils.isEmpty(userInput)) {
    println("Input is empty")
}

Но есть важное отличие: companion object — это реальный объект (синглтон). Вы можете дать ему имя, реализовать интерфейсы, наследоваться от классов. Если имя не указано, по умолчанию он называется Companion.

class MyClass {
    companion object Named {
        fun foo() = "Hello"
    }
}

// Вызов:
MyClass.foo()       // OK
MyClass.Named.foo() // Тоже OK

Лучший подход для утилитных классов: функции верхнего уровня

Для чистых утилитных функций (как ваш StringUtils) в Kotlin лучше вообще не создавать класс. Просто объявите функции на уровне пакета (вне класса). Это называется функциями верхнего уровня (top-level functions).

// Файл: StringUtils.kt
package com.example.utils

fun isEmpty(str: String?): Boolean {
    return str == null || str.trim().isEmpty()
}

fun capitalize(str: String?): String? {
    if (str == null || str.isEmpty()) return str
    return str.substring(0, 1).uppercase() + str.substring(1)
}

// Использование (после импорта):
import com.example.utils.isEmpty
import com.example.utils.capitalize

if (isEmpty(userInput)) {
    println("Input is empty")
}

Это рекомендуемый способ для утилит. Код чище, нет лишнего класса. Из Java эти функции будут видны как статические методы класса StringUtilsKt (по имени файла).

Когда что использовать?

  • Функции верхнего уровня — для утилитных функций, констант, вспомогательных методов. Это идиоматичный Kotlin.
  • companion object — когда функция логически принадлежит классу (например, фабричный метод create()), или когда нужно наследование/интерфейсы.
  • @JvmStatic — если ваш Kotlin-код вызывается из Java и нужно, чтобы метод был именно статическим (а не методом экземпляра Companion).
class MyClass {
    companion object {
        @JvmStatic
        fun fromJava() { }
    }
}

// В Java это будет:
MyClass.fromJava(); // статический метод

Дополнительные фишки Kotlin

  • Константы: используйте const val внутри companion object или на верхнем уровне.
  • Расширения: можно добавить "статические" методы к существующим классам через функции-расширения.
  • Объекты-одиночки (object declaration): если нужен синглтон с методами, используйте object MyUtils { ... }.

Итог: для вашего примера с StringUtils лучший выбор — функции верхнего уровня. Это самый идиоматичный и лаконичный способ в Kotlin.

<<>>

Альтернативный подход: использование object declaration

Если вам нужен не просто набор статических методов, а полноценный объект-синглтон с состоянием (например, кэш или настройки), используйте ключевое слово object:

object StringUtils {
    fun isEmpty(str: String?): Boolean = str.isNullOrBlank()
    fun capitalize(str: String?): String? = str?.replaceFirstChar { it.uppercase() }
}

// Вызов:
StringUtils.isEmpty("test")

Это создаёт единственный экземпляр класса (синглтон). В отличие от companion object, такой объект не привязан к другому классу — он самостоятельный. Из Java будет виден как StringUtils.INSTANCE.isEmpty(...), если не добавить @JvmStatic.

Когда это удобно?

  • Когда нужен mutable state (счётчики, кэш).
  • Когда объект реализует интерфейс (например, компаратор).
  • Когда вы хотите инкапсулировать логику с состоянием, но не хотите создавать класс с конструктором.

Минус: для простых утилит без состояния это избыточно — лучше использовать функции верхнего уровня.

Выбор между companion object и object зависит от контекста: первый — для методов, логически связанных с классом, второй — для самостоятельных синглтонов.