109 votes

Pourquoi et quand utiliser @JvmStatic avec des objets compagnons?

J'essaie de comprendre la différence entre utiliser ou non @JvmStatic, et quand je dois utiliser un.

Ainsi, avec Kotlin et Java, je peux faire ceci:

TestKotlin.kt

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

Qui est alors appelé par Java, comme ceci:

TestJava.java

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

mais ensuite, il y a cette option 2:

TestKotlin.kt v2

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

Et puis, l'appeler à partir de Java, comme ceci:

TestJava.java v2

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

Donc mes questions sont:

  • Sont ces 2 cas différents, en termes de comportement ou d'allocation de mémoire?
  • Est-il une préférence sur laquelle utiliser?
  • Faire à la fois de créer un pseudo-statique objet singleton, comme Java statique n'?

Merci!

109voto

yole Points 3628

Le comportement de l' @JvmStatic d'annotation est expliqué en détail dans la documentation. Lors de la lecture de la documentation, vous devez supposer qu'il vous donne toutes les informations importantes, et les différences de comportement qui ne sont pas mentionnés dans la documentation n'existent pas.

Dans ce cas, la documentation dit:

Si vous utilisez cette annotation, le compilateur va générer à la fois une méthode statique de la classe englobante de l'objet et une méthode d'instance de l'objet lui-même.

En d'autres termes, l'effet de l'annotation, c'est qu'il indique au compilateur de générer une méthode supplémentaire.

La documentation de mentionner qu'il n'y a aucune différence dans le comportement ou l'allocation de la mémoire? Il ne le fait pas. Par conséquent, il est sûr de supposer qu'il n'y est aucune.

Est-il une préférence sur laquelle utiliser? Normalement, une API est déclaré dans un endroit et utilisé à partir de plusieurs endroits. Si vous appelez une méthode de Java, vous devez le déclarer comme @JvmStatic, car l'ajout de l' @JvmStatic d'annotation en un seul endroit vous permettra de le laisser plusieurs .Companion références dans de multiples endroits.

Faire à la fois de créer un pseudo-statique objet singleton, comme Java statique n'? Cette question n'a pas de sens, parce que Java statique ne permet pas de créer un "pseudo-statique objet singleton". Si vous déclarez une méthode statique dans une classe Java, et ensuite appeler cette méthode, pas d'objets seront créés.

16voto

Maddy Points 1628

Vous place de la fonction dans le "compagnon de l'objet".

Ainsi, le code java comme ceci:

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

deviendra

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

Vous pouvez ensuite l'utiliser à partir de l'intérieur de Kotlin code

DemoClass.myMethod();

Mais à l'intérieur de code Java, vous devez l'appeler comme

DemoClass.Companion.myMethod();

(Qui fonctionne également à partir de l'intérieur de Kotlin.)

Si vous n'aimez pas avoir à spécifier l' Companion peu, vous pouvez soit ajouter un @JvmStatic d'annotation ou le nom de votre compagnon de classe.

À partir de la docs:

Compagnon Objets

Une déclaration d'objet à l'intérieur d'une classe peut être marqué avec le compagnon mot-clé:

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

Les membres de la compagne de l'objet peut être appelé en utilisant tout simplement la classe nom de la qualification:

val instance = MyClass.create()

...

Cependant, sur la JVM, vous pouvez avoir des membres de compagnon objets générés comme de véritables méthodes statiques et les champs, si vous utilisez l' @JvmStatic annotation. Voir la Java interopérabilité section pour plus de détails.

L'ajout de l' @JvmStatic d'annotation ressemble à ceci

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

et puis un existera aussi un réel Java fonction statique, accessible à partir de Java et kotlin comme DemoClass.myMethod().

Si c'est juste détesté par l' Companion nom, alors vous pouvez aussi donner un nom explicite pour le compagnon de l'objet ressemble à ceci:

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

qui vous permettra de l'appeler à partir d'Kotlin de la même façon, mais à partir de java comme DemoClass.Blah.myMethod() (qui fonctionne également dans Kotlin).

3voto

s1m0nw1 Points 21698

Dans Kotlin, l' companion objet peut être utilisé pour imiter le comportement statique, les appels ressembler les appels statiques en Java, l' "Companion" ne fait pas partie du si. Si elle est utilisée en Java cependant, l' companion objet doit être nommé, à moins d' @JvmStatic est appliquée. Il avait l'air moins idiomatiques autrement.

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

Indiqué dans les docs:

Compagnon Objets

Une déclaration d'objet à l'intérieur d'une classe peut être marqué avec le compagnon mot-clé:

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

Les membres de la compagne de l'objet peut être appelé en utilisant tout simplement la classe nom de la qualification:

val instance = MyClass.create()

...

Cependant, sur la JVM, vous pouvez avoir des membres de compagnon objets générés comme de véritables méthodes statiques et les champs, si vous utilisez l' @JvmStatic annotation. Voir la Java interopérabilité section pour plus de détails.

Notez qu'il va générer un supplémentaire de la méthode comme indiqué ici:

Si vous utilisez cette annotation, le compilateur va générer à la fois une méthode statique de la classe englobante de l'objet et une méthode d'instance de l'objet lui-même.

Voyons un exemple:

La classe suivante

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

ressemble à ça sur le bytecode niveau, ici représentée comme code Java:

@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();
      }
   }
}

Si @JvmStatic est appliquée à l' callMe méthode bien que, le bytecode modifications suivantes:

@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();
      }
   }
}

Vous pouvez le voir, correctement documentée, la statique callMe de la fonction, dans le cadre de l' Outer est généré:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X