Методы статического расширения в Котлине

142

Как вы определяете метод статического расширения в Kotlin? Это вообще возможно? В настоящее время у меня есть метод расширения, как показано ниже.

public fun Uber.doMagic(context: Context) {
    // ...
}

Вышеупомянутое расширение может быть вызвано на экземпляре.

uberInstance.doMagic(context) // Instance method

но как мне сделать это статическим методом, как показано ниже.

Uber.doMagic(context)         // Static or class method
Рагунатх Джавахар
источник
1
Что вы подразумеваете под "статическим методом расширения"?
Андрей Бреслав
3
Метод, который я могу вызвать без экземпляра, но все еще является расширением класса. (Обычные статические методы Java)
Рагунат Джавахар
Я думаю, что это может быть не целевое использование расширений Kotlin. Хороший вопрос, я думал о том же самом, пытаясь экстраполировать концепцию C #. Но в то время как на практике использование довольно похоже. Расширения Kotlin, в то время как они утверждают, что они статически отправлены, они чувствуют себя динамически «привязанными», если есть такая вещь, или поздно связанными с экземпляром. Если я не ошибаюсь ... или, возможно, я совершенно не понял :)
Дан
7
В настоящее время вы не можете написать метод расширения, который будет вызываться для имени класса, а не для экземпляра класса. Действительно, функция расширения принимает параметр приемника, который является экземпляром, поэтому нет способа пропустить эту часть. С другой стороны, мы работаем над способом написания расширений для объектов классов, чтобы вы вызывали его по имени класса, но у получателя есть тип объекта класса
Андрей Бреслав,
@AndreyBreslav Можете ли вы сообщить нам, будут ли разрешены статические функции расширения? Я тоже скучаю.
Ларс Блумберг

Ответы:

143

Для достижения Uber.doMagic(context), вы можете написать расширение для компаньона объекта из Uber(требуется описание объекта спутника):

class Uber {
    companion object {}
}

fun Uber.Companion.doMagic(context: Context) { }
Андрей Бреслав
источник
79
Это хорошо, но что, если это класс Java или класс без компаньона?
Jire
31
@Jire для таких случаев, нет поддержки в Kotlin 1.0
Андрей Бреслав
2
Я мог видеть случай использования в расширении классов инфраструктуры JVM со значениями по умолчанию, например, String.DEFAULT или Int.DEFAULT, в случае, если этого требует логика вашего домена (моя в настоящее время делает это в сочетании с пустыми классами данных).
Штеффен Функе
36
Это работает только до тех пор, пока Uber является классом Kotlin . Было бы хорошо иметь возможность статически расширять классы Java, а также
kosiara - Bartosz Kosarzycki
6
Это все еще невозможно, как вы можете видеть здесь: youtrack.jetbrains.com/issue/KT-11968 Здесь есть связанная ветка SO: stackoverflow.com/questions/33911457/…
narko
12

Вот что говорится в официальной документации:

Котлин генерирует статические методы для функций уровня пакета. Kotlin также может генерировать статические методы для функций, определенных в именованных объектах или объектах-компаньонах, если вы аннотируете эти функции как @JvmStatic. Например:

Котлин статические методы

class C {
  companion object {
    @JvmStatic fun foo() {}
    fun bar() {}
  }
}

Теперь foo () является статическим в Java, а bar () - нет:

C.foo(); // works fine
C.bar(); // error: not a static method
lucasddaniel
источник
8

У меня действительно был этот точный вопрос 30 минут назад, поэтому я начал копаться и не мог найти никакого решения или обходного пути для этого, НО во время поиска я нашел этот раздел на веб-сайте Kotlinglang, в котором говорится, что:

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

Тогда у меня возникла самая сумасшедшая идея, почему бы не определить функцию расширения с обнуляемым получателем (без фактического использования этого получателя), а затем вызвать ее для нулевого объекта! Я попробовал это, и это сработало довольно хорошо, но выглядело так ужасно. Это было так:

(null as Type?).staticFunction(param1, param2)

Поэтому я обошел это, создав val в моем файле расширений типа приемника со значением null, а затем использовал его в моем другом классе. Итак, в качестве примера, вот как я реализовал «статическую» функцию расширения для Navigationкласса в Android: В моем файле NavigationExtensions.kt:

val SNavigation: Navigation? = null
fun Navigation?.createNavigateOnClickListener(@IdRes resId: Int, args: Bundle? = null, navOptions: NavOptions? = null,
                                                navigationExtras: Navigator.Extras? = null) : (View) -> Unit {
    //This is just implementation details, don't worry too much about them, just focus on the Navigation? part in the method declaration
    return { view: View -> view.navigate(resId, args, navOptions, navigationExtras) }
}

В коде, который использует это:

SNavigation.createNavigateOnClickListener(R.id.action_gameWonFragment_to_gameFragment)

Очевидно, что это не имя класса, это просто переменная типа класса, которая имеет нулевое значение. Это явно уродливо на стороне создателя расширений (потому что они должны создать переменную) и на стороне разработчика (потому что они должны использовать STypeформат вместо фактического имени класса), но это самое близкое, что может быть достигнуто прямо сейчас по сравнению с реальными статическими функциями. Надеемся, что создатели языка Kotlin ответят на созданную проблему и добавят эту функцию в язык.

kyay
источник
2
Хаки это может быть. Но и умный круто. Это самый проницательный ответ на вопрос. Kotlin делает все возможное, чтобы подделать, предоставляя первоклассные расширения, поэтому IMO, вы используете лучшие из возможных решений, чтобы справиться с его глупостями. Ницца.
Трэвис Григгс
5

Вы можете создать статический метод с использованием объекта Companion, например:

class Foo {
    // ...
    companion object {
        public fun bar() {
            // do anything
        }
    }
}

и тогда вы можете назвать это как:

class Baz {
    // ...
    private fun callBar() {
        Foo.bar()
    }
}
bbarm
источник
Я считаю, что это на самом деле не статично. Сопутствующие объекты позволяют вам вызывать, используя имя класса, но все еще используют экземпляр класса. Кроме того, вопрос был о статических функциях расширения, а не только о статических функциях. Однако, чтобы иметь статические функции, вы можете использовать platformStaticаннотацию.
Рагунат Джавахар
1
platformStaticбыл переименован JvmStaticв текущий Котлин.
Джейсон Минард
0

Рекомендую посмотреть на эту ссылку. Как вы можете видеть, вы просто должны объявить метод на верхнем уровне пакета (файла):

package strings
public fun joinToString(...): String { ... }

Это равно

package strings;

public class JoinKt {
    public static String joinToString(...) { ... }
}

С константами все одинаково. Эта декларация

val UNIX_LINE_SEPARATOR = "\n"

равно

public static final String UNIX_LINE_SEPARATOR = "\n";
Nisazh
источник
-3

Мне также очень нравится иметь возможность добавлять статические методы расширения в Kotlin. В качестве временного решения я добавляю метод exntension к нескольким классам вместо использования одного метода статического расширения во всех них.

class Util    

fun Util.isDeviceOnline(context: Context): Boolean {
    val connMgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkInfo = connMgr.activeNetworkInfo
    return networkInfo != null && networkInfo.isConnected
}

fun Activity.isDeviceOnline(context: Context) = { Util().isDeviceOnline(context) }
fun OkHttpClient.isDeviceOnline(context: Context) = { Util().isDeviceOnline(context) }
Косиара - Бартош Косаржицкий
источник
2
Первоначальный вопрос был о том, как javaclass может быть расширен статическим методом . В вашем случае это означает возможность буквально звонить, Activity.isDeviceOnline(...)не имея экземпляра Activity.
Фабиан Цейндл
-4

Чтобы создать метод расширения в kotlin, вы должны создать файл kotlin (не класс), а затем объявить свой метод в файле, например:

public fun String.toLowercase(){
    // **this** is the string object
}

Импортируйте функцию в класс или файл, над которым вы работаете, и используйте ее.

daniel.jbatiz
источник
Титул даже не вопрос
daniel.jbatiz