11

Каков эквивалент статических методов Java в Kotlin?

30

В Kotlin нет ключевого слова static. Каков самый лучший способ представить static метод Java в Kotlin?

5 ответ(ов)

0

A. Старая версия Java:

  1. Объявите companion object, чтобы поместить статический метод / переменную.

    class Foo {
        companion object {
            fun foo() = println("Foo")
            val bar = "bar"
        }
    }
    
  2. Используйте:

    Foo.foo()        // Выводит Foo    
    println(Foo.bar) // Выводит bar
    


B. Новая версия Kotlin:

  1. Объявите функции и переменные напрямую в файле без класса в .kt файле.

    fun foo() = println("Foo")
    val bar = "bar"
    
  2. Используйте методы/переменные по их именам. (После их импорта)

    Используйте:

    foo()        // Выводит Foo          
    println(bar) // Выводит bar     
    

0

В 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 — это удобный способ создания статических методов и свойств.

0

Это тоже сработало для меня.

object Bell {
    @JvmStatic
    fun ring() { }
}

Из Kotlin:

Bell.ring()

Из Java:

Bell.ring();

Таким образом, можно вызывать метод ring() из объекта Bell как из Kotlin, так и из Java, благодаря аннотации @JvmStatic, которая делает метод статическим и доступным в Java.

0

Несмотря на то, что этот вопрос достаточно старый (больше 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. Надеюсь, это поможет!

0

Топ-уровень / 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");

Вот и все! Надеюсь, примеры окажутся полезными для ваших проектов.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь