Статичні методи розширення в Котліні


142

Як визначити метод статичного розширення в Котліні? Це навіть можливо? В даний час у мене є метод розширення, як показано нижче.

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

Вищеописане розширення можна викликати в екземплярі.

uberInstance.doMagic(context) // Instance method

але як зробити це статичним методом, як показано нижче.

Uber.doMagic(context)         // Static or class method

1
Що ви маєте на увазі під «методом статичного розширення»?
Андрій Бреслав

3
Метод, який я можу викликати без екземпляра, але все ще є розширенням класу. (Регулярні статичні методи Java)
Рагунат Джавахар

Я думаю, що це може бути не гарним питанням використання розширень Котліна , я думав про те саме, намагаючись екстраполювати концепцію C #. Але на практиці використання є досить схожим. Розширення Котліна, хоча вони стверджують, що вони статично відправлені, вони відчувають, що вони динамічно "приєднані", якщо є щось таке, або пізно обмежені екземпляром. Якщо я не помиляюся ... або, можливо, я зрозумів це абсолютно неправильно :)
Dan

7
Наразі ви не можете написати метод розширення, який би викликався назви класу на відміну від екземпляра класу. Дійсно, функція розширення приймає параметр приймача, який є екземпляром, тому немає можливості пропустити цю частину. З іншого боку, ми працюємо над способом запису розширень на об’єкти класу, щоб ви називали це ім'ям класу, але приймач має тип об’єкта класу
Андрій Бреслав

@AndreyBreslav Чи можете ви оновити, чи будуть дозволені функції статичного розширення? Я і цього пропускаю.
Ларс Блюмберг

Відповіді:


143

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

class Uber {
    companion object {}
}

fun Uber.Companion.doMagic(context: Context) { }

79
Це добре, але що робити, якщо це клас Java або клас без супутника?
Джире

31
@Жіре для таких випадків, у Котліні 1.0 немає підтримки
Андрій Бреслав

2
Я можу побачити випадок використання в розширенні каркасних класів JVM зі значеннями за замовчуванням, наприклад, String.DEFAULT або Int.DEFAULT, якщо ваша доменна логіка вимагає цього (моє в даний час у поєднанні з порожніми класами даних).
Steffen Funke

36
Це працює лише до тих пір, поки Uber - клас Котліна . Було б добре мати можливість також статично розширювати класи Java
kosiara - Bartosz Kosarzycki

6
Це ще не можливо , як ви можете побачити тут: youtrack.jetbrains.com/issue/KT-11968 Існує споріднений SO нитка тут: stackoverflow.com/questions/33911457 / ...
NARKO

12

Про це говорить офіційна документація:

Котлін генерує статичні методи для функцій на рівні пакетів. Котлін також може генерувати статичні методи для функцій, визначених у названих об'єктах або супутніх об'єктах, якщо ви коментуєте ці функції як @JvmStatic. Наприклад:

Статичні методи Котліна

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

Тепер foo () є статичним на Java, тоді як bar () - не:

C.foo(); // works fine
C.bar(); // error: not a static method

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формат замість фактичного імені класу), але це найближче, що можна досягти зараз порівняно з фактичними статичними функціями. Сподіваємось, виробники мов Котлін відповідуть на проблему, яку створили, і додадуть цю особливість у мові.


2
Хаккі це може бути. Але також розумно круто. Це найпроникливіша відповідь на питання. Kotlin робить все можливе, щоб підробити надання розширень першого класу, тому IMO, ви використовуєте найкращі рішення для боротьби з його загальними рисами. Приємно.
Тревіс Гріггс

5

Ви можете створити статичний метод за допомогою об'єкта Companion, наприклад:

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

і тоді ви можете назвати це так:

class Baz {
    // ...
    private fun callBar() {
        Foo.bar()
    }
}

Я вважаю, що насправді не є статичним. Об'єкти-супутники дозволяють вам дзвонити, використовуючи ім'я класу, але все-таки використовується екземпляр класу. Також питання стосувалося статичних функцій розширення, а не лише статичних функцій. Однак, щоб мати статичні функції, ви можете використовувати 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";

-3

Мені також дуже подобається мати можливість додавати методи статичного розширення в Котлін. На даний момент я додаю метод розширення до декількох класів, а не використовую один метод статичного розширення у всіх.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.