Statyczne metody rozszerzające w Kotlinie

142

Jak zdefiniujesz statyczną metodę rozszerzenia w Kotlinie? Czy to w ogóle możliwe? Obecnie mam metodę rozszerzenia, jak pokazano poniżej.

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

Powyższe rozszerzenie można wywołać w instancji.

uberInstance.doMagic(context) // Instance method

ale jak zrobić to statyczną metodą, jak pokazano poniżej.

Uber.doMagic(context)         // Static or class method
Ragunath Jawahar
źródło
1
Co masz na myśli mówiąc o „statycznej metodzie rozszerzenia”?
Andrey Breslav
3
Metoda, którą mogę wywołać bez instancji, ale nadal jest rozszerzeniem klasy. (Zwykłe metody statyczne w Javie)
Ragunath Jawahar
Myślę, że może to nie być zamierzone użycie rozszerzeń Kotlin. Dobre pytanie, myślałem o tym samym, próbując ekstrapolować koncepcję C #. Ale podczas gdy w praktyce użycie jest dość podobne. Rozszerzenia Kotlin, mimo że twierdzą, że są wysyłane statycznie, czują się jak dynamicznie „dołączone”, jeśli coś takiego istnieje, lub późno związane z instancją. Jeśli się nie mylę ... a może kompletnie się mylę :)
Dan
7
Obecnie nie można napisać metody rozszerzającej, która byłaby wywoływana na podstawie nazwy klasy w przeciwieństwie do instancji klasy. Rzeczywiście, funkcja rozszerzająca przyjmuje parametr odbiornika, który jest instancją, więc nie ma możliwości pominięcia tej części. Z drugiej strony pracujemy nad sposobem pisania rozszerzeń do obiektów klas, tak aby wywoływać to na podstawie nazwy klasy, ale odbiorca ma typ obiektu klasy
Andrey Breslav
@AndreyBreslav Czy możesz nas zaktualizować, czy statyczne funkcje rozszerzeń będą dozwolone? Też mi to brakuje.
Lars Blumberg

Odpowiedzi:

143

Aby osiągnąć Uber.doMagic(context), można napisać rozszerzenie do obiektu towarzysz z Uber(wymagane jest zgłoszenie obiektu towarzysz):

class Uber {
    companion object {}
}

fun Uber.Companion.doMagic(context: Context) { }
Andrey Breslav
źródło
79
To jest fajne, ale co, jeśli jest to klasa Java lub klasa bez elementu towarzyszącego?
Jire
31
@Jire dla takich przypadków, nie ma wsparcia w Kotlin 1.0
Andrey Breslav
2
Widziałem przypadek użycia w rozszerzaniu klas frameworka JVM z wartościami domyślnymi, np. String.DEFAULT lub Int.DEFAULT, na wypadek, gdyby wymagała tego logika domeny (moja obecnie robi to w połączeniu z pustymi klasami danych).
Steffen Funke
36
Działa tylko tak długo, jak długo Uber jest klasą Kotlin . Dobrze by było mieć możliwość statycznego rozszerzania klas Javy
kosiara - Bartosz Kosarzycki
6
Nadal nie jest to możliwe, jak widać tutaj: youtrack.jetbrains.com/issue/KT-11968 Istnieje tutaj powiązany wątek SO: stackoverflow.com/questions/33911457/…
narko
12

Oto, co mówi oficjalna dokumentacja:

Kotlin generuje statyczne metody dla funkcji na poziomie pakietu. Kotlin może również generować statyczne metody dla funkcji zdefiniowanych w nazwanych obiektach lub obiektach towarzyszących, jeśli oznaczysz te funkcje jako @JvmStatic. Na przykład:

Metody statyczne Kotlina

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

Teraz foo () jest statyczne w Javie, podczas gdy bar () nie jest:

C.foo(); // works fine
C.bar(); // error: not a static method
lucasddaniel
źródło
8

Właściwie to miałem dokładnie to pytanie 30 minut temu, więc zacząłem kopać i nie mogłem znaleźć żadnego rozwiązania ani obejścia tego problemu, ALE podczas wyszukiwania znalazłem tę sekcję na stronie Kotlinglang, która stwierdza, że:

Należy zauważyć, że rozszerzenia można zdefiniować za pomocą typu odbiornika dopuszczającego wartość null. Takie rozszerzenia można wywołać w zmiennej obiektowej, nawet jeśli jej wartość jest równa null.

Więc wtedy wpadłem na najbardziej szalony pomysł, dlaczego nie zdefiniować funkcji rozszerzającej z odbiornikiem dopuszczającym wartość null (bez faktycznego używania tego odbiornika), a następnie wywołać ją na obiekcie zerowym! Spróbowałem tego i działało całkiem nieźle, ale wyglądało tak brzydko. To było tak:

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

Więc obejrzałem to, tworząc w moim pliku rozszerzeń wartość typu odbiornika, która miała wartość null, a następnie użyłem jej w mojej innej klasie. Oto przykład, jak zaimplementowałem „statyczną” funkcję rozszerzenia dla Navigationklasy w systemie Android: W moim pliku 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) }
}

W kodzie, który go używa:

SNavigation.createNavigateOnClickListener(R.id.action_gameWonFragment_to_gameFragment)

Oczywiście nie jest to nazwa klasy, to po prostu zmienna typu klasy, która ma wartość null. Jest to oczywiście brzydkie po stronie kreatora rozszerzeń (ponieważ muszą utworzyć zmienną) i po stronie programisty (ponieważ muszą używać STypeformatu zamiast rzeczywistej nazwy klasy), ale jest to najbliższe, co można teraz osiągnąć w porównaniu z rzeczywistymi funkcjami statycznymi. Miejmy nadzieję, że twórcy języka Kotlin odpowiedzą na powstały problem i dodadzą tę funkcję do języka.

kyay
źródło
2
To może być hacky. Ale także sprytny fajny. To najbardziej wnikliwa odpowiedź na to pytanie. Kotlin robi wszystko, co w jego mocy, aby udawać, że dostarcza najwyższej klasy rozszerzenia, więc IMO używa najlepszych rozwiązań, aby poradzić sobie z jego niedoskonałościami. Miły.
Travis Griggs
5

Możesz stworzyć metodę statyczną za pomocą obiektu Companion, takiego jak:

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

a potem możesz to nazwać tak:

class Baz {
    // ...
    private fun callBar() {
        Foo.bar()
    }
}
bbarm
źródło
Uważam, że w rzeczywistości nie jest to statyczne. Obiekty towarzyszące umożliwiają wywoływanie przy użyciu nazwy klasy, ale nadal używają instancji tej klasy. Ponadto pytanie dotyczyło statycznych funkcji rozszerzających, a nie tylko statycznych funkcji. Jednak aby mieć funkcje statyczne, możesz użyć platformStaticadnotacji.
Ragunath Jawahar
1
platformStaticzostał przemianowany JvmStaticw obecnym Kotlinie.
Jayson Minard
0

Polecam spojrzeć na ten link. Jak widzisz, powinieneś po prostu zadeklarować metodę na najwyższym poziomie pakietu (pliku):

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

To jest równe

package strings;

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

W przypadku constans wszystko jest takie samo. Ta deklaracja

val UNIX_LINE_SEPARATOR = "\n"

jest równe

public static final String UNIX_LINE_SEPARATOR = "\n";
Nisazh
źródło
-3

Bardzo lubię również możliwość dodawania statycznych metod rozszerzających w Kotlinie. Aby obejść ten problem, dodaję metodę rozszerzenia do wielu klas, zamiast używać jednej statycznej metody rozszerzającej we wszystkich.

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) }
kosiara - Bartosz Kosarzycki
źródło
2
Pierwotne pytanie dotyczyło sposobu rozszerzenia javaclass za pomocą metody statycznej . W twoim przypadku oznacza to możliwość wywołania dosłownie Activity.isDeviceOnline(...)bez wystąpienia Activity.
Fabian Zeindl
-4

Aby utworzyć metodę rozszerzenia w kotlin, musisz utworzyć plik kotlin (nie klasę), a następnie zadeklarować swoją metodę w pliku Np .:

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

Zaimportuj funkcję z klasy lub pliku, nad którym pracujesz, i użyj jej.

daniel.jbatiz
źródło
Tytuł to nawet nie pytanie
daniel.jbatiz