Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why and when to use @JvmStatic with companion objects?

I'm trying to understand the difference between using/not using @JvmStatic, and when I should use either one.

So, with Kotlin and Java, I can do this:

TestKotlin.kt

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

Which is then called by Java, like this:

TestJava.java

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

but then, there's this option 2:

TestKotlin.kt v2

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

And then, call it from Java, like this:

TestJava.java v2

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

So my questions are:

  • Are these 2 cases any different, in terms of behavior or memory allocation?
  • Is there a preference on which one to use?
  • Do both create a pseudo static singleton object, like Java static does?

Thanks!

like image 244
TooManyEduardos Avatar asked Feb 14 '18 05:02

TooManyEduardos


People also ask

What is use of @JvmStatic in Kotlin?

Put simply, this annotation tells the Kotlin compiler to generate one additional static method for the annotated function under the hood. Moreover, the most important use case for this annotation is, of course, better Java interoperability.

Why do we use companion object?

A companion object and its class can access each other's private members. A companion object's apply method lets you create new instances of a class without using the new keyword. A companion object's unapply method lets you de-construct an instance of a class into its individual components.

Why do we use companion object as a kind of replacement for Java static fields in Kotlin?

Solution: companion objectThe ability to extend interfaces and classes is one of the features that sets the companion objects apart from Java's static functionality. Also, companions are objects, we can pass them around to the functions and assign them to variables just like all the other objects in Kotlin.

What is difference between companion object and object?

A few important points to remember while defining companion objects are: Companion objects cannot be declared outside a class. Companion objects can be used similar to static classes in other programming languages like Java and C#. These objects are common in all instances of the class.


2 Answers

The behavior of the @JvmStatic annotation is explained in detail in the documentation. When reading the documentation, you should assume that it gives you all the important information, and behavior differences that are not mentioned in the documentation do not exist.

In this case, the documentation says:

If you use this annotation, the compiler will generate both a static method in the enclosing class of the object and an instance method in the object itself.

In other words, the effect of the annotation is that it tells the compiler to generate an additional method.

Does the documentation mention that there is any difference in behavior or memory allocation? It does not. Therefore, it's safe to assume that there is none.

Is there a preference on which one to use? Normally, an API is declared in one place and used from multiple places. If you're calling a method from Java, then you should declare it as @JvmStatic, because adding the @JvmStatic annotation in one place will allow you to leave out multiple .Companion references in multiple places.

Do both create a pseudo static singleton object, like Java static does? This question does not make sense, because Java static does not create a "pseudo static singleton object". If you declare a static method in a Java class, and then call this method, no objects will be created.

like image 185
yole Avatar answered Sep 19 '22 05:09

yole


A companion object is an instance of a real class called Companion. So, when you call the Kotlin code from Java, an object of the Companion class is first instantiated behind the scenes. To understand this, let's consider a simple example.


Behind the scenes without @JvmStatic

Kotlin code

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

Decompiled 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() { }    } } 

As you can see in the simplified decompiled Java code above, a class named Companion is generated to represent the companion object. The class Plant holds the singleton instance new Plant.Companion() of the class Plant.Companion. The instance is also named as Companion. This is the reason you need to call the functions/properties of the companion object in Java using the Plant.Companion:

Plant.Companion.waterAll(); 

Behind the scenes with @JvmStatic

Kotlin code

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

Decompiled 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() { }    } } 

When you annotate a function of a companion object with @JvmStatic in Kotlin, a pure static function waterAll() is generated in addition to the non static function waterAll(). So, now you are able to call the function without the Companion name which is more idiomatic to Java:

Plant.waterAll(); 

Singleton

The singleton pattern is generated in both cases. As you can see, in both cases, the Companion instance holds the singleton object new Plant.Companion() and the constructor is made private to prevent multiple instances.

The Java static keyword does not create the singletons. You will get the singleton feature only if you create a companion object in Kotlin and then use it from Java. To get singleton from Java, you'll need to write the singleton pattern, the code for which looks like the decompiled Java code shown above.


Performance

There is no performance gain or loss in terms of memory allocation. The reason is that, as you can see in the code above, the extra static function that is generated delegates its work to the non static function Companion.waterAll(). This means, creation of the Companion instance is required in both the cases, with @JvmStatic as well as without @JvmStatic.

The behaviour of both the setups is the same apart from the extra method that is generated. In Android, if you worry about the method count, you may need to keep an eye on this because an extra copy is created for each annotated function.


When to use @JvmStatic

When you know that your Kotlin code won't be used in Java, you don't have to worry about adding the @JvmStatic annotation. This keeps your code cleaner. However, if your Kotlin code is called from Java, it makes sense to add the annotation. This will prevent your Java code from polluting with the name Companion everywhere.

It's not like an additional keyword on either side. If you add @JvmStatic in one place, you can prevent writing the extra Companion word in thousands of places, wherever you call that function. This is especially useful for library creators, if they add @JvmStatic in their Kotlin library, the users of that library won't have to use the Companion word in their Java code.


That's it! Hopefully that helps get the clearer picture of the @JvmStatic.

like image 23
Yogesh Umesh Vaity Avatar answered Sep 19 '22 05:09

Yogesh Umesh Vaity