Statische Erweiterungsmethoden in Kotlin


141

Wie definieren Sie eine statische Erweiterungsmethode in Kotlin? Ist das überhaupt möglich? Ich habe derzeit eine Erweiterungsmethode wie unten gezeigt.

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

Die obige Erweiterung kann für eine Instanz aufgerufen werden.

uberInstance.doMagic(context) // Instance method

aber wie mache ich es statische Methode wie unten gezeigt.

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

1
Was meinst du mit "statische Erweiterungsmethode"?
Andrey Breslav

3
Eine Methode, die ich ohne Instanz aufrufen kann, aber immer noch eine Erweiterung der Klasse ist. (
Ragunath Jawahar

Ich denke, es ist möglicherweise nicht die beabsichtigte Verwendung der Kotlin-Erweiterungen. Gute Frage. Ich habe über das Gleiche nachgedacht, als ich versucht habe, das C # -Konzept zu extrapolieren. In der Praxis ist die Verwendung jedoch ziemlich ähnlich. Kotlin-Erweiterungen geben zwar an, statisch versendet zu sein, fühlen sich jedoch dynamisch "verbunden", wenn es so etwas gibt, oder sind spät an die Instanz gebunden. Wenn ich mich nicht irre ... oder vielleicht habe ich es völlig falsch verstanden :)
Dan

7
Derzeit können Sie keine Erweiterungsmethode schreiben, die für einen Klassennamen im Gegensatz zur Klasseninstanz aufgerufen wird. In der Tat verwendet die Erweiterungsfunktion einen Empfängerparameter , der eine Instanz ist, sodass dieser Teil nicht übersprungen werden kann. Auf der anderen Seite arbeiten wir an einer Möglichkeit, Erweiterungen für Klassenobjekte zu schreiben, sodass Sie sie unter einem Klassennamen aufrufen, aber der Empfänger hat den Typ des Klassenobjekts
Andrey Breslav

@AndreyBreslav Können Sie uns aktualisieren, ob statische Erweiterungsfunktionen zulässig sind? Ich vermisse es auch.
Lars Blumberg

Antworten:


142

Um dies zu erreichen Uber.doMagic(context), können Sie eine Erweiterung in das Begleitobjekt von schreiben Uber(die Deklaration des Begleitobjekts ist erforderlich):

class Uber {
    companion object {}
}

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

78
Das ist schön, aber was ist, wenn es sich um eine Java-Klasse oder eine Klasse ohne Begleiter handelt?
Jire

31
@ Jire für solche Fälle gibt es keine Unterstützung in Kotlin 1.0
Andrey Breslav

2
Ich könnte einen Anwendungsfall beim Erweitern von JVM-Framework-Klassen mit Standardwerten sehen, z. B. String.DEFAULT oder Int.DEFAULT, falls Ihre Domänenlogik dies erfordert (meine derzeit in Verbindung mit leeren Datenklassen).
Steffen Funke

36
Es funktioniert nur, solange Uber eine Kotlin- Klasse ist. Es wäre gut, die Möglichkeit zu haben, auch Java- Klassen statisch zu erweitern
kosiara - Bartosz Kosarzycki

6
Dies ist immer noch nicht möglich, wie Sie hier sehen können: youtrack.jetbrains.com/issue/KT-11968 Hier gibt es einen verwandten SO-Thread: stackoverflow.com/questions/33911457/…
narko

12

Dies ist, was die offizielle Dokumentation sagt:

Kotlin generiert statische Methoden für Funktionen auf Paketebene. Kotlin kann auch statische Methoden für Funktionen generieren, die in benannten Objekten oder Begleitobjekten definiert sind, wenn Sie diese Funktionen als @JvmStatic kommentieren. Beispielsweise:

Kotlin statische Methoden

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

Jetzt ist foo () in Java statisch, während bar () nicht:

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

8

Ich hatte genau diese Frage vor 30 Minuten, also fing ich an zu graben und konnte keine Lösung oder Problemumgehung dafür finden, ABER während der Suche fand ich diesen Abschnitt auf der Kotlinglang-Website, der besagt:

Beachten Sie, dass Erweiterungen mit einem nullbaren Empfängertyp definiert werden können. Solche Erweiterungen können für eine Objektvariable aufgerufen werden, auch wenn ihr Wert null ist.

Also hatte ich die verrückteste Idee aller Zeiten, warum nicht eine Erweiterungsfunktion mit einem nullbaren Empfänger definieren (ohne diesen Empfänger tatsächlich zu verwenden) und sie dann für ein Nullobjekt aufrufen! Also habe ich das versucht und es hat ziemlich gut funktioniert, aber es sah so hässlich aus. Es war so:

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

Also habe ich das umgangen, indem ich in meiner Erweiterungsdatei vom Empfängertyp einen Wert mit dem Wert null erstellt und ihn dann in meiner anderen Klasse verwendet habe. Als Beispiel habe ich hier eine "statische" Erweiterungsfunktion für die NavigationKlasse in Android implementiert : In meiner NavigationExtensions.kt-Datei:

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) }
}

In dem Code, der es verwendet:

SNavigation.createNavigateOnClickListener(R.id.action_gameWonFragment_to_gameFragment)

Offensichtlich ist dies kein Klassenname, sondern nur eine Variable des Klassentyps, die einen Nullwert hat. Dies ist offensichtlich hässlich auf der Seite des Erweiterungsherstellers (weil sie die Variable erstellen müssen) und auf der Entwicklerseite (weil sie das STypeFormat anstelle des tatsächlichen Klassennamens verwenden müssen), aber es ist das nächste, das derzeit erreicht werden kann im Vergleich zu tatsächlichen statischen Funktionen. Hoffentlich werden die Kotlin-Sprachhersteller auf das erstellte Problem reagieren und diese Funktion in die Sprache aufnehmen.


2
Hacky das kann sein. Aber auch clever cool. Dies ist die aufschlussreichste Antwort auf die Frage. Kotlin tut sein Bestes, um die Bereitstellung erstklassiger Erweiterungen zu fälschen. IMO, Sie verwenden die bestmöglichen Lösungen, um mit seinen Irrtümern umzugehen. Nett.
Travis Griggs

5

Sie können eine statische Methode erstellen, indem Sie das Companion-Objekt wie folgt verwenden:

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

und dann kann man es so nennen:

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

Ich glaube das ist eigentlich nicht statisch. Mit Companion-Objekten können Sie unter Verwendung des Klassennamens aufrufen, verwenden jedoch weiterhin eine Instanz der Klasse. Die Frage betraf auch statische Erweiterungsfunktionen, nicht nur statische Funktionen. Um jedoch statische Funktionen zu haben, können Sie die platformStaticAnmerkung verwenden.
Ragunath Jawahar

1
platformStaticwurde JvmStaticim aktuellen Kotlin umbenannt.
Jayson Minard

0

Wir empfehlen Ihnen, sich diesen Link anzusehen . Wie Sie dort sehen können, sollten Sie die Methode nur auf der obersten Ebene des Pakets (der Datei) deklarieren:

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

Dies ist gleich

package strings;

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

Bei Konstanten ist alles gleich. Diese Erklärung

val UNIX_LINE_SEPARATOR = "\n"

entspricht

public static final String UNIX_LINE_SEPARATOR = "\n";

-3

Ich mag es auch sehr, die Möglichkeit zu haben, statische Erweiterungsmethoden in Kotlin hinzuzufügen. Um dies zu umgehen, füge ich die Erweiterungsmethode mehreren Klassen hinzu, anstatt in allen eine statische Erweiterungsmethode zu verwenden.

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
Die ursprüngliche Frage war, wie eine Javaclass mit einer statischen Methode erweitert werden kann . In Ihrem Fall bedeutet dies, dass Sie buchstäblich anrufen können, Activity.isDeviceOnline(...)ohne eine Instanz von zu haben Activity.
Fabian Zeindl

-4

Um eine Erweiterungsmethode in Kotlin zu erstellen, müssen Sie eine Kotlin-Datei (keine Klasse) erstellen und dann Ihre Methode in der Datei deklarieren. ZB:

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

Importieren Sie die Funktion in die Klasse oder Datei, an der Sie arbeiten, und verwenden Sie sie.


Titel ist nicht einmal eine Frage
daniel.jbatiz
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.