web-dev-qa-db-fra.com

Pourquoi et quand utiliser @JvmStatic avec des objets compagnons?

J'essaie de comprendre la différence entre utiliser/ne pas utiliser @JvmStatic et quand je devrais utiliser l'un ou l'autre.

Donc, avec Kotlin et Java, je peux le faire:

TestKotlin.kt

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

Ce qui s'appelle alors par Java, comme ceci:

TestJava.Java

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

mais alors, il y a cette option 2:

TestKotlin.kt v2

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

Et puis, appelez-le de Java, comme ceci:

TestJava.Java v2

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

Donc mes questions sont:

  • Ces 2 cas sont-ils différents, en termes de comportement ou d’allocation de mémoire?
  • Y a-t-il une préférence sur laquelle utiliser?
  • Est-ce que les deux créent un objet singleton pseudo statique, comme Java statique fait?

Merci!

28
TooManyEduardos

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

Dans ce cas, la documentation indique:

Si vous utilisez cette annotation, le compilateur générera une méthode statique dans la classe englobante de l'objet et une méthode d'instance dans l'objet même.

En d'autres termes, l'annotation a pour effet d'indiquer au compilateur de générer une méthode supplémentaire .

La documentation mentionne-t-elle qu'il existe une différence de comportement ou d'allocation de mémoire? Ce ne est pas. Par conséquent, il est prudent de supposer qu'il n'y en a pas.

Y a-t-il une préférence sur laquelle utiliser? Normalement, une API est déclarée à un endroit et utilisée à partir de plusieurs endroits. Si vous appelez une méthode depuis Java, vous devez alors la déclarer comme @JvmStatic, Car l'ajout de l'annotation @JvmStatic À un endroit vous permettra d'omettre plusieurs références .Companion. à plusieurs endroits.

Est-ce que les deux créent un objet singleton pseudo statique, comme le fait Java statique? Cette question n'a pas de sens, car Java static ne crée pas d '"objet singleton pseudo statique". Si vous déclarez une méthode statique dans une classe Java, puis appelez cette méthode, aucun objet ne sera créé.

34
yole

Vous placez la fonction dans "l'objet compagnon".

Donc, le code Java) ressemble à ceci:

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

va devenir

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

Vous pouvez ensuite l'utiliser depuis le code Kotlin en tant que

DemoClass.myMethod();

Mais à partir de Java, vous devez l’appeler comme suit:

DemoClass.Companion.myMethod();

(Ce qui fonctionne aussi depuis Kotlin.)

Si vous n'aimez pas avoir à spécifier le bit Companion, vous pouvez ajouter une annotation @JvmStatic Ou nommer votre classe d'accompagnement.

De la docs :

Objets Compagnon

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

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

Les membres de l'objet compagnon peuvent être appelés en utilisant simplement le nom de la classe comme qualificatif:

val instance = MyClass.create()

...

Toutefois, sur la machine virtuelle Java, vous pouvez générer des membres d’objets associés sous forme de méthodes et de champs statiques réels, si vous utilisez l’annotation @JvmStatic. Voir la section relative à l'interopérabilité Java) pour plus de détails.

L'ajout de l'annotation @JvmStatic Ressemble à ceci

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

et ensuite a existera sous la forme d'une fonction statique réelle Java, accessible à partir de Java et de kotlin sous la forme DemoClass.myMethod(). _.

Si le nom Companion ne vous plaît pas, vous pouvez également donner un nom explicite à l'objet compagnon qui ressemble à ceci:

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

ce qui vous permettra de l'appeler de Kotlin de la même manière, mais à partir de Java comme DemoClass.Blah.myMethod()] _ (qui fonctionnera également dans Kotlin).

2
Maddy

En Kotlin, l’objet companion peut être utilisé pour imiter un comportement statique, les appels ressemblent à des appels statiques en Java, le “Companion“ ne fait pas partie de if. S'il est utilisé dans Java cependant, l'objet companion doit être nommé, à moins que @JvmStatic est appliqué. Sinon, ça aurait l'air moins idiomatique.

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

Énoncé dans le docs :

Objets Compagnon

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

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

Les membres de l'objet compagnon peuvent être appelés en utilisant simplement le nom de la classe comme qualificatif:

val instance = MyClass.create()

...

Toutefois, sur la machine virtuelle Java, vous pouvez générer des membres d’objets associés sous forme de méthodes et de champs statiques réels, si vous utilisez la commande @JvmStatic annotation. Voir la section relative à l'interopérabilité Java) pour plus de détails.

Notez qu'il générera une méthode supplémentaire comme indiqué ici :

Si vous utilisez cette annotation, le compilateur générera une méthode statique dans la classe englobante de l'objet et une méthode d'instance dans l'objet même.

Voyons un exemple :

La classe suivante

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

ressemble à ceci au niveau du bytecode, représenté ici par 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é à la méthode callMe, cependant, le bytecode devient le suivant:

@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 voir, correctement documenté, la fonction statique callMe, dans le cadre de Outer est générée:

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