Warum und wann sollte @JvmStatic mit Companion-Objekten verwendet werden?

Lesezeit: 8 Minuten

Benutzer-Avatar
TooManyEduardos

Ich versuche, den Unterschied zwischen der Verwendung/Nichtverwendung von @JvmStatic zu verstehen und wann ich beide verwenden sollte.

Mit Kotlin und Java kann ich also Folgendes tun:

TestKotlin.kt

class TestKotlin {
    companion object {
        val someString = "hello world"
    }
}

Was dann von Java so aufgerufen wird:

TestJava.java

public class TestJava {
    String kotlinStaticString = TestKotlin.Companion.getSomeString();
}

aber dann gibt es diese Option 2:

TestKotlin.kt v2

class TestKotlin {
    companion object {
        @JvmStatic  // <-- notice the @JvmStatic annotation
        val someString = "hello world"
    }
}

Rufen Sie es dann wie folgt aus Java auf:

TestJava.java v2

public class TestJava {
    String kotlinStaticString = TestKotlin.getSomeString();
}

Also meine Fragen sind:

  • Unterscheiden sich diese beiden Fälle in Bezug auf das Verhalten oder die Speicherzuweisung?
  • Gibt es eine Präferenz, welche verwendet werden soll?
  • Erstellen beide ein pseudostatisches Singleton-Objekt, wie es Java Static tut?

Vielen Dank!

Benutzer-Avatar
du

Das Verhalten der @JvmStatic Annotation wird ausführlich in erläutert die Dokumentation. Beim Lesen der Dokumentation sollten Sie davon ausgehen, dass Sie alle wichtigen Informationen erhalten und in der Dokumentation nicht erwähnte Verhaltensunterschiede nicht bestehen.

In diesem Fall heißt es in der Dokumentation:

Wenn Sie diese Annotation verwenden, generiert der Compiler sowohl eine statische Methode in der einschließenden Klasse des Objekts als auch eine Instanzmethode im Objekt selbst.

Mit anderen Worten, die Anmerkung bewirkt, dass sie den Compiler anweist Generieren Sie eine zusätzliche Methode.

Erwähnt die Dokumentation, dass es Unterschiede im Verhalten oder in der Speicherzuweisung gibt? Es tut nicht. Daher kann man davon ausgehen, dass es keine gibt.

Gibt es eine Präferenz, welche verwendet werden soll? Normalerweise wird eine API an einer Stelle deklariert und von mehreren Stellen verwendet. Wenn Sie eine Methode aus Java aufrufen, sollten Sie sie als deklarieren @JvmStaticweil das Hinzufügen der @JvmStatic Annotation an einer Stelle ermöglicht es Ihnen, mehrere wegzulassen .Companion Verweise an mehreren Stellen.

Erstellen beide ein pseudostatisches Singleton-Objekt, wie es Java Static tut? Diese Frage ist nicht sinnvoll, da Java Static kein “pseudostatisches Singleton-Objekt” erstellt. Wenn Sie eine statische Methode in einer Java-Klasse deklarieren und diese Methode dann aufrufen, werden keine Objekte erstellt.

  • @TooManyEduardos “… Speicherzuweisung” – ja. Sie haben überall, wo Sie versuchen (oder müssen) zu implementieren, ein rudimentäres Begleitobjekt staatenlos “Util”-Klasse und Sie tun es in “Java-Idiom”. Dieser Gedanke ist in der Frage stackoverflow.com/q/54104719/3926506 dargestellt

    – Sergej Bubenschtschikow

    9. Januar 2019 um 7:12 Uhr

EIN companion object ist eine Instanz eines reellen class genannt Companion. Wenn Sie also den Kotlin-Code von Java aufrufen, wird ein Objekt der Companion Klasse wird zuerst hinter den Kulissen instanziiert. Um dies zu verstehen, betrachten wir ein einfaches Beispiel.


Hinter den Kulissen ohne @JvmStatic

Kotlin-Code

class Plant {
    companion object {
        fun waterAll() { }
    }
}

Dekompilierter Java-Code

public final class Plant {

   public static final Plant.Companion Companion = new Plant.Companion();

   public static final class Companion {

      public final void waterAll() { }

      private Companion() { }
   }
}

Wie Sie im vereinfachten dekompilierten Java-Code oben sehen können, ist eine Klasse namens Companion wird generiert, um die darzustellen companion object. Die Klasse Plant enthält die Singleton-Instanz new Plant.Companion() der Klasse Plant.Companion. Die Instanz wird auch als benannt Companion. Aus diesem Grund müssen Sie die Funktionen/Eigenschaften von aufrufen companion object in Java mit der Plant.Companion:

Plant.Companion.waterAll();

Hinter den Kulissen mit @JvmStatic

Kotlin-Code

class Plant {
    companion object {
        @JvmStatic
        fun waterAll() { }
    }
}

Dekompilierter Java-Code

public final class Plant {

   public static final Plant.Companion Companion = new Plant.Companion();

   @JvmStatic
   public static final void waterAll() { Companion.waterAll();}

   public static final class Companion {
      @JvmStatic
      public final void waterAll() { }

      private Companion() { }
   }
}

Wenn Sie eine Funktion von a kommentieren companion object mit @JvmStatic in Kotlin, ein reines static Funktion waterAll() wird zusätzlich zur nicht statischen Funktion generiert waterAll(). Jetzt können Sie die Funktion also ohne die aufrufen Companion Name, der für Java idiomatischer ist:

Plant.waterAll();

Einzelling

Das Singleton-Muster wird in beiden Fällen generiert. Wie Sie sehen können, ist in beiden Fällen die Companion -Instanz enthält das Singleton-Objekt new Plant.Companion() und der Konstruktor ist gemacht private um mehrere Instanzen zu verhindern.

Das Java static Das Schlüsselwort erstellt keine Singletons. Sie erhalten die Singleton-Funktion nur, wenn Sie eine erstellen companion object in Kotlin und verwenden Sie es dann von Java aus. Um Singleton aus Java zu erhalten, müssen Sie das Singleton-Muster schreiben, dessen Code wie der oben gezeigte dekompilierte Java-Code aussieht.


Leistung

Es gibt keinen Leistungsgewinn oder -verlust in Bezug auf die Speicherzuweisung. Der Grund dafür ist, wie Sie im obigen Code sehen können, das Extra static Die generierte Funktion delegiert ihre Arbeit an die nicht statische Funktion Companion.waterAll(). Das bedeutet, Erstellung der Companion Beispiel ist in beiden Fällen erforderlich, mit @JvmStatic als auch ohne @JvmStatic.

Das Verhalten beider Setups ist abgesehen von der zusätzlichen Methode, die generiert wird, gleich. Wenn Sie sich in Android Sorgen um die Anzahl der Methoden machen, müssen Sie dies möglicherweise im Auge behalten, da für jede annotierte Funktion eine zusätzliche Kopie erstellt wird.


Wann verwenden @JvmStatic

Wenn Sie wissen, dass Ihr Kotlin-Code nicht in Java verwendet wird, müssen Sie sich keine Gedanken über das Hinzufügen von machen @JvmStatic Anmerkung. Dadurch bleibt Ihr Code sauberer. Wenn Ihr Kotlin-Code jedoch aus Java aufgerufen wird, ist es sinnvoll, die Annotation hinzuzufügen. Dadurch wird verhindert, dass Ihr Java-Code den Namen verschmutzt Companion überall, überallhin, allerorts.

Es ist nicht wie ein zusätzliches Schlüsselwort auf beiden Seiten. Wenn Sie hinzufügen @JvmStatic An einer Stelle können Sie verhindern, dass Sie das Extra schreiben Companion Wort an Tausenden von Stellen, wo immer Sie diese Funktion aufrufen. Dies ist besonders nützlich für Ersteller von Bibliotheken, wenn sie hinzufügen @JvmStatic in ihrer Kotlin-Bibliothek müssen die Benutzer dieser Bibliothek die nicht verwenden Companion Wort in ihrem Java-Code.


Das ist es! Hoffentlich hilft das dabei, sich ein klareres Bild zu machen @JvmStatic.

  • perfekte Erklärung mit den dekompilierten Java-Beispielen

    – htafoya

    9. April 2021 um 23:07 Uhr

  • Die gute Erklärung, die alle Ins und Outs abdeckte

    – M.Muzammil

    15. Januar um 8:52

  • Kotlin-Philosophie: Alles ist Kotlin.

    – Jeffrey Blattmann

    13. Mai um 0:15 Uhr

Sie platzieren die Funktion im “Begleitobjekt”.

Also der Java-Code wie folgt:

class DemoClass {
  public static int myMethod() { return 1; }
}

wird werden

class DemoClass {
  companion object {
     fun myMethod() : Int = 1
  }
}

Sie können es dann innerhalb von Kotlin-Code als verwenden

DemoClass.myMethod();

Aber innerhalb von Java-Code müssten Sie es als aufrufen

DemoClass.Companion.myMethod();

(Was auch innerhalb von Kotlin funktioniert.)

Wenn Sie das nicht angeben möchten Companion Bit können Sie entweder a hinzufügen @JvmStatic Anmerkung oder benennen Sie Ihre Companion-Klasse.

Von dem Dokumente:

Begleitobjekte

Eine Objektdeklaration innerhalb einer Klasse kann mit dem begleitenden Schlüsselwort gekennzeichnet werden:

class MyClass {
   companion object Factory {
       fun create(): MyClass = MyClass()
   }
}

Mitglieder des Begleitobjekts können aufgerufen werden, indem einfach der Klassenname als Qualifizierer verwendet wird:

val instance = MyClass.create()

Auf der JVM können Sie jedoch Mitglieder von Begleitobjekten als echte statische Methoden und Felder generieren lassen, wenn Sie die verwenden @JvmStatic
Anmerkung. Weitere Einzelheiten finden Sie im Abschnitt Java-Interoperabilität.

Hinzufügen der @JvmStatic Anmerkung sieht so aus

class DemoClass {
  companion object {
    @JvmStatic
    fun myMethod() : Int = 1;
  }
}

und dann wird a als echte statische Java-Funktion existieren, auf die sowohl von Java als auch von Kotlin aus zugegriffen werden kann DemoClass.myMethod().

Wenn es nur von den nicht gemocht wird Companion Name, dann können Sie auch einen expliziten Namen für das Companion-Objekt angeben, der so aussieht:

class DemoClass {
  companion object Blah {
    fun myMethod() : Int = 1;
  }
}

wodurch Sie es auf die gleiche Weise von Kotlin aus aufrufen können, aber von Java wie DemoClass.Blah.myMethod() (was auch in Kotlin funktionieren wird).

  • Ja, ich weiß, wie man es so oder so benutzt. Ich möchte wissen, ob es einen Unterschied zwischen ihnen gibt, hauptsächlich im Umgang mit dem Speicher

    – TooManyEduardos

    14. Februar 2018 um 6:04 Uhr

Benutzer-Avatar
s1m0nw1

In Kotlin, der companion Objekt kann verwendet werden, um statisches Verhalten zu imitieren, Aufrufe sehen aus wie statische Aufrufe in Java, die “Companion“ ist nicht Teil von if. Wenn es jedoch in Java verwendet wird, ist die companion Objekt muss benannt werden, es sei denn @JvmStatic wird angewandt. Sonst würde es weniger idiomatisch aussehen.

TestKotlin.getSomeString() //this should be preferred whenever possible

Angegeben in der Dokumente:

Begleitobjekte

Eine Objektdeklaration innerhalb einer Klasse kann mit dem begleitenden Schlüsselwort gekennzeichnet werden:

class MyClass {
   companion object Factory {
       fun create(): MyClass = MyClass()
   }
}

Mitglieder des Begleitobjekts können aufgerufen werden, indem einfach der Klassenname als Qualifizierer verwendet wird:

val instance = MyClass.create()

Auf der JVM können Sie jedoch Mitglieder von Begleitobjekten als echte statische Methoden und Felder generieren lassen, wenn Sie die verwenden @JvmStatic
Anmerkung. Weitere Einzelheiten finden Sie im Abschnitt Java-Interoperabilität.

Beachten Sie, dass es eine generiert zusätzlich Methode wie angegeben hier:

Wenn Sie diese Annotation verwenden, generiert der Compiler sowohl eine statische Methode in der einschließenden Klasse des Objekts als auch eine Instanzmethode im Objekt selbst.

Mal sehen Beispiel:

Die folgende Klasse

class Outer {
    companion object {
        fun callMe() = ""
    }
}

sieht auf Bytecode-Ebene so aus, hier dargestellt als Java-Code:

@Metadata(...)
public final class Outer {
   public static final Outer.Companion Companion = new Outer.Companion((DefaultConstructorMarker)null);

   @Metadata(...)
   public static final class Companion {
      @NotNull
      public final String callMe() {
         return "";
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

Wenn @JvmStatic angewendet wird callMe -Methode ändert sich der Bytecode jedoch wie folgt:

@Metadata(...)
public final class Outer {
   public static final Outer.Companion Companion = new Outer.Companion((DefaultConstructorMarker)null);

   @JvmStatic
   @NotNull
   public static final String callMe() {
      return Companion.callMe();
   }

   @Metadata(...)
   public static final class Companion {
      @JvmStatic
      @NotNull
      public final String callMe() {
         return "";
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

Sie können, korrekt dokumentiert, die Statik sehen callMe Funktion, als Teil von Outer wird generiert:

@JvmStatic
@NotNull
public static final String callMe() {        
    return Companion.callMe();
}

1256810cookie-checkWarum und wann sollte @JvmStatic mit Companion-Objekten verwendet werden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy