Если вы пришли из мира Java, вы уже знакомы со статическими методами. Они не позволяют нам копировать наши методы в каждый класс и позволяют использовать их без создания объекта. Но что, если я скажу вам, что в Котлине нет статических методов ?!

Правда. Но это не означает, что мы не можем воспользоваться преимуществами статических методов в Kotlin. Есть способ их обойти. Посмотрим как.

В документации Kotlin рекомендуется использовать функции уровня пакета, если вы хотите следовать DRY. Это может показаться немного странным, если вы работали с Java, потому что в Java ее нет! Давайте создадим функцию на уровне пакета в Kotlin. Все, что вам нужно сделать, это создать файл с расширением .kt и поместить метод, который вы будете использовать, в нескольких местах. Я создам файл с именем SampleClass.kt. Содержимое SampleClass.kt:

package packageA
fun bar(){
    println("Method bar")
}

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

public final class SameClassKt {
   public static final void bar() {
      String var0 = "Method bar";
      System.out.println(var0);
   }
}

Несмотря на то, что мы не помещали его ни в какое объявление класса, Kotlin при компиляции создаст класс с именем SampleClassKt и поместит в него метод. И обратите внимание на сигнатуру нашего метода: public static final void bar () Именно то, что мы хотели иметь!

Если вы хотите вызвать эту функцию из любого другого класса, скажем HelloWorld.kt, вы можете вызвать ее, выполнив:

package packageB
import packageA.bar
fun main(args: Array<String>) {
    bar()
}

Обратите внимание, что мы должны импортировать этот метод из его пакета, отсюда и оператор import packageA.bar.

Другой способ сделать это - поместить метод в объявление объекта.

package packageA
object Foo{
    fun bar() = println("Method bar")
    var foo="foo"
}

И вы можете получить к нему доступ, например:

Foo.bar()

Объявление объекта в основном используется внутри класса, оно полезно, если вы хотите получить доступ к внутренним компонентам класса. Чтобы лучше понять это, давайте посмотрим его декомпилированный код:

public final class Foo {
   public static final Foo INSTANCE;

   public final void bar() {
      String var1 = "Method bar";
      System.out.println(var1);
   }

   private Foo() {
      INSTANCE = (Foo)this;
   }

   static {
      new Foo();
   }
}

Любые методы или переменные в объявлении объекта будут работать как статический метод или переменная.

Если вы посмотрите на приведенный выше код, у нас есть статический блок, который будет выполняться, когда класс загружается в память, этот блок создает объект Foo. Обратите внимание, что конструктор является частным, поэтому объект может быть создан только внутри класса. Звонит в колокол? Таким образом мы получаем класс Singleton!

Но если вы хотите сделать это так же, как в старом стиле Java, то есть используя имя класса в качестве квалификатора, вам понадобится идентификатор компаньона.

class SampleClass{
    companion object {
        fun bar()= print("Bar method")
    }
}

А затем вы можете получить к нему доступ из кода Kotlin, используя тот же синтаксис, что и в Java. т.е.

SampleClass.foo()

Следует отметить, что если вы вызываете метод под объектом-компаньоном из кода Java, вам нужно будет поместить идентификатор сопутствующего объявления между именем класса и именем метода. например

SampleClass.Companion.foo();

Если вы не хотите помещать идентификатор компаньона между ними, вы также можете дать имя своему объекту

companion object Foo{
    fun foo()= print("In foo method")
}

А затем вызовите его с помощью SampleClass.Foo.foo ().

Если вы хотите избавиться от идентификатора Companion, а также не хотите давать какое-либо имя своему объекту, вам необходимо разместить аннотацию @JvmStatic с именем метода.

class SampleClass{
    companion object {
        @JvmStatic
        fun foo()= print("In foo method")
    }
}

Тогда вы также можете вызвать его как SampleClass.foo () из кода Java.

Прежде чем закрыть эту тему, давайте посмотрим, как разрешать функции расширения статически. Если вы не знакомы с функциями расширения, попросту говоря, это методы, определенные вне класса, но ведут себя так, как если бы они были функциями-членами. Посмотрите пример ниже:

fun main(args: Array<String>) {
    val sampleClass=SampleClass()
    sampleClass.foo()
}
class SampleClass{
}
private fun SampleClass.foo() {
    println("saas")
}

Как видите, мы обращались к методу foo (), как если бы он был частью SampleClass.

В любом случае, теперь мы попытаемся получить доступ к функциям расширения статически. Для этого нам нужно поместить в класс пустой объект-компаньон.

class SampleClass{
    companion object
}

Чтобы определить функцию расширения, которая может вызываться статически, нам нужно поместить идентификатор Companion между классом получателя и именем метода:

private fun SampleClass.Companion.foo() {
    println("saas")
}

Теперь мы можем вызвать функцию расширения, не создавая объект класса получателя:

SampleClass.foo()

Если вы посмотрите на декомпилированный код, то увидите, что это почти то, что мы хотели:

private static final void foo(@NotNull SampleClass.Companion $receiver) {
   String var1 = "saas";
   System.out.println(var1);
}

Полезные ссылки:

Документация Котлина



Блог команды JetBrains, обсуждающей статические константы в Kotlin.



Отличная ветка abreslav о статическом разрешении функции расширения.