Каков эквивалент статических методов Java в Kotlin?
В Kotlin нет ключевого слова static
. Каков самый лучший способ представить static
метод Java в Kotlin?
5 ответ(ов)
A. Старая версия Java:
Объявите
companion object
, чтобы поместить статический метод / переменную.class Foo { companion object { fun foo() = println("Foo") val bar = "bar" } }
Используйте:
Foo.foo() // Выводит Foo println(Foo.bar) // Выводит bar
B. Новая версия Kotlin:
Объявите функции и переменные напрямую в файле без класса в
.kt
файле.fun foo() = println("Foo") val bar = "bar"
Используйте
методы/переменные
по их именам. (После их импорта)Используйте:
foo() // Выводит Foo println(bar) // Выводит bar
В Kotlin вы можете использовать object для представления статических свойств и методов, что позволяет избежать необходимости создавать одиночный класс (singleton). При этом, если вам нужно сделать статические элементы внутри класса, вы можете использовать companion object.
Вот пример использования object
:
object Abc {
fun sum(a: Int, b: Int): Int = a + b
}
Если вам необходимо вызвать этот метод из Java, то код будет выглядеть следующим образом:
int z = Abc.INSTANCE.sum(x, y);
Обратите внимание, что в Kotlin вы можете просто вызывать метод без использования INSTANCE
:
val result = Abc.sum(x, y)
Таким образом, object
в Kotlin — это удобный способ создания статических методов и свойств.
Это тоже сработало для меня.
object Bell {
@JvmStatic
fun ring() { }
}
Из Kotlin:
Bell.ring()
Из Java:
Bell.ring();
Таким образом, можно вызывать метод ring()
из объекта Bell
как из Kotlin, так и из Java, благодаря аннотации @JvmStatic
, которая делает метод статическим и доступным в Java.
Несмотря на то, что этот вопрос достаточно старый (больше 2 лет), и на него уже даны множество отличных ответов, я заметил, что некоторые другие способы получения "статических" полей в Kotlin отсутствуют. Вот примерный гид по взаимодействию Kotlin и Java с static
:
Сценарий 1: Создание статического метода в Kotlin для Java
Kotlin
@file:JvmName("KotlinClass") // Это задает имя для файла, чтобы оно по умолчанию не было [KotlinClassKt] в Java package com.frybits class KotlinClass { companion object { // Эта аннотация говорит Java считать этот метод статическим для [KotlinClass] @JvmStatic fun foo(): Int = 1 // Без нее вам пришлось бы использовать [KotlinClass.Companion.bar()] для вызова этого метода. fun bar(): Int = 2 } }
Java
package com.frybits; class JavaClass { void someFunction() { println(KotlinClass.foo()); // Выводит "1" println(KotlinClass.Companion.bar()); // Выводит "2". Это единственный способ использовать [bar()] в Java. println(KotlinClass.Companion.foo()); // Чтобы показать, что [Companion] все еще держит метод [foo()] } void println(Object o) { System.out.println(o); } }
Ответ Майкла Андерсона предоставляет больше глубины по этой теме и должен быть обязательно упомянут для данного сценария.
Следующий сценарий объясняет, как создавать статические переменные в Kotlin, чтобы Java не приходилось постоянно вызывать KotlinClass.foo()
в случаях, когда статическая функция не нужна.
Сценарий 2: Создание статической переменной в Kotlin для Java
Kotlin
@file:JvmName("KotlinClass") // Это задает имя для файла, чтобы оно по умолчанию не было [KotlinClassKt] в Java package com.frybits class KotlinClass { companion object { // Эта аннотация говорит Kotlin не генерировать getter/setter функции в Java. Вместо этого переменная должна быть доступна напрямую // Это похоже на [@JvmStatic], так как она говорит Java рассматривать это как статическую переменную для [KotlinClass]. @JvmField var foo: Int = 1 // Если хотите что-то вроде [final static], и значение является примитивом или строкой, можно использовать ключевое слово [const] const val dog: Int = 1 // Это будет рассматриваться только как член объекта [Companion]. Для него генерируются getter/setter. var bar: Int = 2 // Мы все еще можем использовать [@JvmStatic] для 'var' переменных, но это генерирует getter/setter как функции KotlinClass @JvmStatic var cat: Int = 9 } }
Java
package com.frybits; class JavaClass { void someFunction() { // Пример использования @JvmField println(KotlinClass.foo); // Выводит "1" KotlinClass.foo = 3; // Пример использования 'const val' println(KotlinClass.dog); // Выводит "1". Обратите внимание на отсутствие функции getter // Пример, когда не используются ни @JvmField, ни @JvmStatic, ни 'const val' println(KotlinClass.Companion.getBar()); // Выводит "2" KotlinClass.Companion.setBar(3); // Setter для [bar] // Пример использования @JvmStatic вместо @JvmField println(KotlinClass.getCat()); KotlinClass.setCat(0); } void println(Object o) { System.out.println(o); } }
Одной из замечательных особенностей Kotlin является возможность создания верхнеуровневых функций и переменных. Это дает возможность создавать "бесклассовые" списки константных полей и функций, которые могут быть использованы как static
функции/поля в Java.
Сценарий 3: Доступ к верхнеуровневым полям и функциям в Kotlin из Java
Kotlin
// В этом примере имя файла "KSample.kt". Если эта аннотация не была бы указана, все функции и поля пришлось бы получать // с использованием имени [KSampleKt.foo()] в Java. Упростите себе жизнь и назовите это что-то более простое @file:JvmName("KotlinUtils") package com.frybits const val TAG = "Ты это!" var foo = 1 @JvmField var bar = 2 val GENERATED_VAL: Long = "123".toLong() fun doSomethingAwesome() { println("Все классно!") }
Java
package com.frybits; class JavaClass { void someFunction() { println(KotlinUtils.TAG); // Пример вывода [TAG] // Пример, когда не используется @JvmField. println(KotlinUtils.getFoo()); // Выводит "1" KotlinUtils.setFoo(3); // Пример использования @JvmField println(KotlinUtils.bar); // Выводит "2". Обратите внимание на отсутствие функции getter KotlinUtils.bar = 3; // Поскольку это верхнеуровневая переменная, аннотации не нужны для использования println(KotlinUtils.getGENERATED_VAL()); // Так выглядит доступ к верхнеуровневой функции KotlinUtils.doSomethingAwesome(); } void println(Object o) { System.out.println(o); } }
Еще одно замечательное упоминание, которое можно использовать в Java как "статические" поля, - это Kotlin object
классы. Это одноразовые классы (синглтонов), которые инициализируются лениво при первом использовании. Больше информации о них можно найти здесь: https://kotlinlang.org/docs/reference/object-declarations.html#object-declarations
Однако для доступа к синглтону создается специальный объект INSTANCE
, что не менее удобно, чем работать с Companion
. Вот как использовать аннотации для придания чистого static
ощущения в Java:
Сценарий 4: Использование
object
классовKotlin
@file:JvmName("KotlinClass") package com.frybits object KotlinClass { // Нет необходимости использовать ключевое слово 'class'. const val foo: Int = 1 @JvmStatic var cat: Int = 9 @JvmStatic fun getCustomClassName(): String = this::class.java.simpleName + "boo!" var bar: Int = 2 fun someOtherFunction() = "Что такое 'INSTANCE'?" }
Java
package com.frybits; class JavaClass { void someFunction() { println(KotlinClass.foo); // Прямой доступ к [foo] в синглтоне [KotlinClass] println(KotlinClass.getCat()); // Getter для [cat] KotlinClass.setCat(0); // Setter для [cat] println(KotlinClass.getCustomClassName()); // Пример использования функции этого 'object' класса println(KotlinClass.INSTANCE.getBar()); // Вот как выглядит доступ к синглтону без использования аннотаций KotlinClass.INSTANCE.setBar(23); println(KotlinClass.INSTANCE.someOtherFunction()); // Доступ к функции в классе объекта без аннотаций } void println(Object o) { System.out.println(o); } }
Теперь у вас есть несколько способов работы со "статическими" полями и функциями Kotlin из Java. Надеюсь, это поможет!
Топ-уровень / companion object
для static
свойств
Топ-уровень
Когда свойства имеют отношение к классу, определите их как свойства верхнего уровня прямо перед объявлением класса:
const val MAX_ATTEMPTS = 3
private const val DEFAULT_NAME = "Guest"
private const val MIN_AGE = 16
data class User(val id: String, val name: String = DEFAULT_NAME)
Это аналогично static
свойствам в Java.
Когда свойства полностью независимы от любого класса, их можно определить как верхнеуровневые в отдельном файле без класса.
companion object
Когда свойства тесно связаны с классом и будут использоваться только в этом классе, определите их внутри companion object
:
data class User(val id: String, val name: String = DEFAULT_NAME) {
companion object {
const val DEFAULT_NAME = "Guest"
const val MIN_AGE = 16
}
}
Топ-уровень / companion object
для static
методов
Топ-уровень
Похожим образом, когда функции имеют некоторое отношение к классу, определите их прямо перед классом:
fun getAllUsers() { }
fun getProfileFor(userId: String) { }
data class User(val id: String, val name: String)
Использование:
val userList = getAllUsers()
companion object
Когда функции тесно связаны с классом, определите их внутри companion object
:
data class User(val id: String, val name: String) {
companion object {
fun getAll() { }
fun profileFor(userId: String) { }
}
}
Использование:
val userProfile = User.profileFor("34")
Это эквивалентно static
методам в Java.
Функции верхнего уровня часто более идиоматичны для Kotlin. Более веской причиной для определения функций внутри companion object
является расширение companion object
с помощью интерфейса
. Пример этого показан в разделе про синглтоны.
Вложенный класс для static
класса
Когда классы, содержащие связанные функциональности, должны быть объединены, их можно сгруппировать, используя вложенность:
class User(val id: String, val name: String) {
class UserAccess : UserDao {
override fun add(user: User) { }
override fun remove(id: String) { }
}
}
Это соответствует static
вложенным классам в Java. Класс UserAccess
здесь реализует интерфейс UserDao
.
Использование:
fun main() {
val john = User("34", "John")
val userAccess = User.UserAccess()
userAccess.add(john)
}
Синглтон object
для static INSTANCE
Топ-уровень
Когда вам нужен только один объект класса, вам уже не нужно создавать static INSTANCE
внутри класса, как это делается в Java. Просто используйте верхнеуровневое объявление object
:
object UserAccess : UserDao {
override fun add(user: User) { }
override fun remove(id: String) { }
}
Также обратите внимание, насколько легко расширять интерфейс или класс в синглтоне.
Код выше фактически создает следующий паттерн синглтона static INSTANCE
в Java (упрощенный вариант):
public final class UserAccess implements UserDao {
public static final UserAccess INSTANCE;
public void add(User user) { }
public void remove(String id) { }
private UserAccess() { }
static { INSTANCE = new UserAccess();}
}
companion object
Когда синглтон тесно связан с классом, используйте companion object
:
data class User(val id: String, val name: String) {
companion object : UserDao {
override fun add(user: User) { }
override fun remove(id: String) { }
}
}
Таким образом, вы получаете более элегантные названия: User.add(john)
. Также вы четко указываете, что этот синглтон используется только как утилита для класса User
. Вы также можете использовать object
без ключевого слова companion
внутри класса, если хотите иметь несколько синглтонов или групп функций/свойств.
companion object
для static
фабрики
Фабричные функции в Kotlin создаются с использованием companion object
. Фабричные функции полезны, когда вы хотите предоставить несколько способов создания объекта, когда процесс создания объекта сложен или когда несколько конструкторов не являются достаточными.
Например, фабричная функция newInstance()
в следующем фрагменте создает пользователя, автоматически генерируя id
:
class User private constructor(val id: Long, val name: String) {
companion object {
private var currentId = 0L
fun newInstance(name: String) = User(currentId++, name)
}
}
Это эквивалентно static
фабричным методам в Java.
Конструктор остается private
, но companion object
имеет доступ к конструктору.
В приведенном выше коде гарантируется последовательность генерации следующего id
, так как companion object
является синглтоном, только один объект будет отслеживать id
, так что дубликатов id
не будет.
Также обратите внимание, что у companion object могут быть свойства (в данном случае currentId
), представляющие состояние.
Использование:
val john = User.newInstance("John")
@JvmStatic
для совместимости с Java
Статическая концепция Java не существует в Kotlin. companion object
является экземпляром реального класса
, называемого Companion
. Поэтому, когда вы вызываете код Kotlin из Java, объект класса Companion
сначала инстанцируется за кулисами. Вам нужно вызывать функцию с использованием объекта Companion
в Java:
Profile userProfile = User.Companion.profileFor("34");
Для идиоматичного Java именования и уменьшения многословия используйте аннотацию @JvmStatic
для этой функции или свойства:
companion object {
@JvmStatic
fun profileFor(userId: String): Profile { }
}
Аннотация @JvmStatic
создает отдельную "чистую" static
копию функции getProfileFor()
. Теперь вы можете использовать её из Java с обычным синтаксисом:
Profile userProfile = User.profileFor("34");
Вот и все! Надеюсь, примеры окажутся полезными для ваших проектов.
Установить курсор в конец текста в EditText
Когда следует использовать статические методы
Как работают сервлеты? Инстанцирование, сессии, общие переменные и многопоточность
Возможные значения конфигурации hbm2ddl.auto в Hibernate и их назначение
Что такое PECS (Producer Extends Consumer Super)?