java 为什么以及何时将 @JvmStatic 与伴生对象一起使用?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48780003/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 10:09:28  来源:igfitidea点击:

Why and when to use @JvmStatic with companion objects?

javaandroidkotlinkotlin-companion

提问by TooManyEduardos

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

我试图了解使用/不使用@JvmStatic 以及何时应该使用其中之一之间的区别。

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

因此,使用 Kotlin 和 Java,我可以这样做:

TestKotlin.kt

TestKotlin.kt

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

Which is then called by Java, like this:

然后由 Java 调用,如下所示:

TestJava.java

TestJava.java

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

but then, there's this option 2:

但是,有这个选项 2:

TestKotlin.ktv2

TestKotlin.ktv2

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

And then, call it from Java, like this:

然后,从 Java 调用它,如下所示:

TestJava.javav2

TestJava.javav2

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?
  • 这两种情况在行为或内存分配方面有什么不同吗?
  • 是否有偏好使用哪个?
  • 是否都创建了一个伪静态单例对象,就像 Java static 那样?

Thanks!

谢谢!

回答by yole

The behavior of the @JvmStaticannotation 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.

@JvmStatic注释的行为在文档中详细解释。在阅读文档时,您应该假设它为您提供了所有重要信息,并且文档中未提及的行为差异不存在。

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 @JvmStaticannotation in one place will allow you to leave out multiple .Companionreferences in multiple places.

是否有偏好使用哪个?通常,一个 API 是在一个地方声明的,并在多个地方使用。如果您从 Java 调用一个方法,那么您应该将其声明为@JvmStatic,因为@JvmStatic在一个地方添加注释将允许您.Companion在多个地方省略多个引用。

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.

是否都创建了一个伪静态单例对象,就像 Java static 那样?这个问题没有意义,因为 Java static 不会创建“伪静态单例对象”。如果在 Java 类中声明静态方法,然后调用该方法,则不会创建任何对象。

回答by Maddy

You place the function in the "companion object".

您将函数放置在“伴随对象”中。

So the java code like this:

所以java代码是这样的:

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

will become

会变成

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

You can then use it from inside Kotlin code as

然后您可以在 Kotlin 代码中使用它作为

DemoClass.myMethod();

But from within Java code, you would need to call it as

但是在 Java 代码中,您需要将其称为

DemoClass.Companion.myMethod();

(Which also works from within Kotlin.)

(这也适用于 Kotlin 内部。)

If you don't like having to specify the Companionbit you can either add a @JvmStaticannotation or name your companion class.

如果您不想指定Companion位,您可以添加@JvmStatic注释或命名您的伴生类。

From the docs:

文档

Companion Objects

An object declaration inside a class can be marked with the companion keyword:

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

Members of the companion object can be called by using simply the class name as the qualifier:

val instance = MyClass.create()

...

However, on the JVM you can have members of companion objects generated as real static methods and fields, if you use the @JvmStaticannotation. See the Java interoperability section for more details.

伴随对象

类中的对象声明可以用伴随关键字标记:

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

可以通过简单地使用类名作为限定符来调用伴生对象的成员:

val instance = MyClass.create()

...

但是,在 JVM 上,如果使用@JvmStatic注释,您可以将伴随对象的成员生成为真正的静态方法和字段。有关更多详细信息,请参阅 Java 互操作性部分。

Adding the @JvmStaticannotation looks like this

添加@JvmStatic注释看起来像这样

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

and then a will exist as a real Java static function, accessible from both Java and kotlin as DemoClass.myMethod().

然后 a 将作为真正的 Java 静态函数存在,可以从 Java 和 kotlin 作为DemoClass.myMethod().

If it is just disliked by the Companionname, then you can also provide an explicit name for the companion object looks like this:

如果只是因为Companion名称不喜欢它,那么您还可以为伴随对象提供一个显式名称,如下所示:

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

which will let you call it from Kotlin in the same way, but from java like DemoClass.Blah.myMethod()(which will also work in Kotlin).

这将让您以相同的方式从 Kotlin 调用它,但是从 java like DemoClass.Blah.myMethod()(它也可以在 Kotlin 中工作)。

回答by s1m0nw1

In Kotlin, the companionobject can be us used to imitate static behaviour, calls look like static calls in Java, the “Companion“isn't part of if. If used in Java though, the companionobject has to be named, unless @JvmStaticis applied. It'd look less idiomatic otherwise.

在 Kotlin 中,companion对象可以用来模仿静态行为,调用看起来像 Java 中的静态调用,“Companion“不是 if 的一部分。但是,如果在 Java 中使用,则companion必须命名对象,除非@JvmStatic已应用。否则它看起来不那么惯用了。

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

Stated in the docs:

文档中说明

Companion Objects

An object declaration inside a class can be marked with the companion keyword:

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

Members of the companion object can be called by using simply the class name as the qualifier:

val instance = MyClass.create()

...

However, on the JVM you can have members of companion objects generated as real static methods and fields, if you use the @JvmStaticannotation. See the Java interoperability section for more details.

伴随对象

类中的对象声明可以用伴随关键字标记:

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

可以通过简单地使用类名作为限定符来调用伴生对象的成员:

val instance = MyClass.create()

...

但是,在 JVM 上,如果使用@JvmStatic注释,您可以将伴随对象的成员生成为真正的静态方法和字段。有关更多详细信息,请参阅 Java 互操作性部分。

Note that it will generate an additionalmethod as stated here:

请注意,它会产生一个额外的说明方法,在这里

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.

如果使用这个注解,编译器将在对象的封闭类中生成一个静态方法,在对象本身中生成一个实例方法。

Let's see an example:

让我们看一个例子

The following class

下面的课

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

looks like this on bytecode level, here represented as Java 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();
      }
   }
}

If @JvmStaticis being applied to callMemethod though, the bytecode changes to the following:

如果@JvmStatic应用于callMe方法,则字节码更改为以下内容:

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

You can see, correctly documented, the static callMefunction, as part of Outeris generated:

您可以看到,正确记录的静态callMe函数,作为Outer生成的一部分:

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