为什么以及何时将 @JvmStatic 与伴随对象一起使用?
Why and when to use @JvmStatic with companion objects?
我正在尝试了解 using/not 使用 @JvmStatic 之间的区别,以及何时应该使用其中任何一个。
所以,有了 Kotlin 和 Java,我可以这样做:
TestKotlin.kt
class TestKotlin {
companion object {
val someString = "hello world"
}
}
然后由 Java 调用,如下所示:
TestJava.java
public class TestJava {
String kotlinStaticString = TestKotlin.Companion.getSomeString();
}
但是,还有这个选项 2:
TestKotlin.kt
v2
class TestKotlin {
companion object {
@JvmStatic // <-- notice the @JvmStatic annotation
val someString = "hello world"
}
}
然后,从 Java 调用它,如下所示:
TestJava.java
v2
public class TestJava {
String kotlinStaticString = TestKotlin.getSomeString();
}
所以我的问题是:
- 这两种情况在行为或内存分配方面有什么不同吗?
- 是否有使用哪一个的偏好?
- 是否都像 Java static 那样创建伪静态单例对象?
谢谢!
在 Kotlin 中,companion
对象可以用来模仿静态行为,调用看起来像 Java 中的静态调用,“Companion“
不是 if 的一部分。如果在 Java 中使用,则必须命名 companion
对象,除非应用 @JvmStatic
。否则它看起来不那么惯用。
TestKotlin.getSomeString() //this should be preferred whenever possible
在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 @JvmStatic
annotation. See the Java interoperability section for more details.
请注意,它将生成一个 附加 方法,如 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.
让我们看一个例子:
以下class
class Outer {
companion object {
fun callMe() = ""
}
}
在字节码级别上看起来像这样,这里表示为 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();
}
}
}
如果 @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();
}
}
}
您可以看到,正确记录了静态 callMe
函数,作为 Outer
的一部分生成:
@JvmStatic
@NotNull
public static final String callMe() {
return Companion.callMe();
}
您将函数放在 "companion object"。
所以 java 代码是这样的:
class DemoClass {
public static int myMethod() { return 1; }
}
会变成
class DemoClass {
companion object {
fun myMethod() : Int = 1
}
}
然后您可以在 Kotlin 代码中使用它作为
DemoClass.myMethod();
但是在 Java 代码中,您需要将其称为
DemoClass.Companion.myMethod();
(也适用于 Kotlin。)
如果您不喜欢必须指定 Companion
位,您可以添加 @JvmStatic
注释或将您的同伴命名为 class.
来自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 @JvmStatic
annotation. See the Java interoperability section for more details.
添加 @JvmStatic
注释如下所示
class DemoClass {
companion object {
@JvmStatic
fun myMethod() : Int = 1;
}
}
然后 a 将作为真正的 Java 静态函数存在,可从
Java 和 kotlin 都为 DemoClass.myMethod()
。
如果只是被Companion
这个名字嫌弃,那你也可以
为伴随对象提供一个明确的名称,如下所示:
class DemoClass {
companion object Blah {
fun myMethod() : Int = 1;
}
}
这将让您以相同的方式从 Kotlin 调用它,但是
来自 java like DemoClass.Blah.myMethod()
(这也适用于 Kotlin)。
@JvmStatic
注释的行为在 the documentation 中有详细解释。阅读文档时,您应该假设它为您提供了所有重要信息,并且不存在文档中未提及的行为差异。
在这种情况下,文档说:
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.
换句话说,注解的作用是告诉编译器生成一个额外的方法。
文档中是否提到在行为或内存分配方面存在任何差异?它不是。因此,可以安全地假设存在 none.
使用哪一个有偏好吗?通常,API 在一个地方声明并从多个地方使用。如果您从 Java 调用一个方法,那么您应该将其声明为 @JvmStatic
,因为在一个地方添加 @JvmStatic
注释将允许您省略多个 .Companion
在多个地方引用。
是否都像 Java static 那样创建伪静态单例对象?这个问题没有意义,因为Java static 不会创建"pseudo static singleton object"。如果在Javaclass中声明一个静态方法,然后调用这个方法,则不会创建任何对象。
A companion object
是名为 Companion
的真实 class
的一个实例。因此,当您从 Java 调用 Kotlin 代码时,Companion
class 的对象首先在幕后实例化。为了理解这一点,让我们考虑一个简单的例子。
幕后无@JvmStatic
科特林代码
class Plant {
companion object {
fun waterAll() { }
}
}
反编译Java代码
public final class Plant {
public static final Plant.Companion Companion = new Plant.Companion();
public static final class Companion {
public final void waterAll() { }
private Companion() { }
}
}
正如您在上面的简化反编译Java代码中看到的,生成了一个名为Companion
的class来表示companion object
。 class Plant
包含 class Plant.Companion
的单例实例 new Plant.Companion()
。该实例也被命名为 Companion
。这就是您需要使用 Plant.Companion
:
在 Java 中调用 companion object
的 functions/properties 的原因
Plant.Companion.waterAll();
在幕后与 @JvmStatic
科特林代码
class Plant {
companion object {
@JvmStatic
fun waterAll() { }
}
}
反编译Java代码
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() { }
}
}
当你在 Kotlin 中用 @JvmStatic
注释一个 companion object
的函数时,除了非静态函数 [=] 之外,还会生成一个纯 static
函数 waterAll()
33=]。因此,现在您可以在不使用 Companion
名称的情况下调用该函数,这更符合 Java:
Plant.waterAll();
单例
两种情况下都会生成单例模式。如您所见,在这两种情况下,Companion
实例都包含单例对象 new Plant.Companion()
并且构造函数 private
以防止多个实例。
Java static
关键字不创建单例。只有在 Kotlin 中创建 companion object
然后从 Java 中使用它,您才会获得单例功能。要从 Java 获取单例,您需要编写单例模式,其代码看起来像上面显示的反编译 Java 代码。
性能
在内存分配方面没有性能提升或损失。原因是,正如您在上面的代码中看到的,生成的额外 static
函数将其工作委托给非静态函数 Companion.waterAll()
。这意味着,在两种情况下都需要创建 Companion
实例,使用 @JvmStatic
以及不使用 @JvmStatic
.
除了生成的额外方法外,两种设置的行为相同。在Android中,如果您担心方法计数,您可能需要留意这一点,因为为每个注释函数创建了一个额外的副本。
何时使用 @JvmStatic
当您知道您的 Kotlin 代码不会在 Java 中使用时,您不必担心添加 @JvmStatic
注释。这使您的代码更清晰。但是,如果您的 Kotlin 代码是从 Java 调用的,则添加注释是有意义的。这将防止您的 Java 代码随处被名称 Companion
污染。
它不像任何一方的附加关键字。如果在一个地方添加 @JvmStatic
,则可以防止在数千个地方写入额外的 Companion
单词,无论您在何处调用该函数。这对库创建者特别有用,如果他们在他们的 Kotlin 库中添加 @JvmStatic
,该库的用户将不必在他们的 Java 代码中使用 Companion
词。
就是这样!希望这有助于更清楚地了解 @JvmStatic
.
我正在尝试了解 using/not 使用 @JvmStatic 之间的区别,以及何时应该使用其中任何一个。
所以,有了 Kotlin 和 Java,我可以这样做:
TestKotlin.kt
class TestKotlin {
companion object {
val someString = "hello world"
}
}
然后由 Java 调用,如下所示:
TestJava.java
public class TestJava {
String kotlinStaticString = TestKotlin.Companion.getSomeString();
}
但是,还有这个选项 2:
TestKotlin.kt
v2
class TestKotlin {
companion object {
@JvmStatic // <-- notice the @JvmStatic annotation
val someString = "hello world"
}
}
然后,从 Java 调用它,如下所示:
TestJava.java
v2
public class TestJava {
String kotlinStaticString = TestKotlin.getSomeString();
}
所以我的问题是:
- 这两种情况在行为或内存分配方面有什么不同吗?
- 是否有使用哪一个的偏好?
- 是否都像 Java static 那样创建伪静态单例对象?
谢谢!
在 Kotlin 中,companion
对象可以用来模仿静态行为,调用看起来像 Java 中的静态调用,“Companion“
不是 if 的一部分。如果在 Java 中使用,则必须命名 companion
对象,除非应用 @JvmStatic
。否则它看起来不那么惯用。
TestKotlin.getSomeString() //this should be preferred whenever possible
在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
@JvmStatic
annotation. See the Java interoperability section for more details.
请注意,它将生成一个 附加 方法,如 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.
让我们看一个例子:
以下class
class Outer {
companion object {
fun callMe() = ""
}
}
在字节码级别上看起来像这样,这里表示为 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();
}
}
}
如果 @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();
}
}
}
您可以看到,正确记录了静态 callMe
函数,作为 Outer
的一部分生成:
@JvmStatic
@NotNull
public static final String callMe() {
return Companion.callMe();
}
您将函数放在 "companion object"。
所以 java 代码是这样的:
class DemoClass {
public static int myMethod() { return 1; }
}
会变成
class DemoClass {
companion object {
fun myMethod() : Int = 1
}
}
然后您可以在 Kotlin 代码中使用它作为
DemoClass.myMethod();
但是在 Java 代码中,您需要将其称为
DemoClass.Companion.myMethod();
(也适用于 Kotlin。)
如果您不喜欢必须指定 Companion
位,您可以添加 @JvmStatic
注释或将您的同伴命名为 class.
来自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
@JvmStatic
annotation. See the Java interoperability section for more details.
添加 @JvmStatic
注释如下所示
class DemoClass {
companion object {
@JvmStatic
fun myMethod() : Int = 1;
}
}
然后 a 将作为真正的 Java 静态函数存在,可从
Java 和 kotlin 都为 DemoClass.myMethod()
。
如果只是被Companion
这个名字嫌弃,那你也可以
为伴随对象提供一个明确的名称,如下所示:
class DemoClass {
companion object Blah {
fun myMethod() : Int = 1;
}
}
这将让您以相同的方式从 Kotlin 调用它,但是
来自 java like DemoClass.Blah.myMethod()
(这也适用于 Kotlin)。
@JvmStatic
注释的行为在 the documentation 中有详细解释。阅读文档时,您应该假设它为您提供了所有重要信息,并且不存在文档中未提及的行为差异。
在这种情况下,文档说:
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.
换句话说,注解的作用是告诉编译器生成一个额外的方法。
文档中是否提到在行为或内存分配方面存在任何差异?它不是。因此,可以安全地假设存在 none.
使用哪一个有偏好吗?通常,API 在一个地方声明并从多个地方使用。如果您从 Java 调用一个方法,那么您应该将其声明为 @JvmStatic
,因为在一个地方添加 @JvmStatic
注释将允许您省略多个 .Companion
在多个地方引用。
是否都像 Java static 那样创建伪静态单例对象?这个问题没有意义,因为Java static 不会创建"pseudo static singleton object"。如果在Javaclass中声明一个静态方法,然后调用这个方法,则不会创建任何对象。
A companion object
是名为 Companion
的真实 class
的一个实例。因此,当您从 Java 调用 Kotlin 代码时,Companion
class 的对象首先在幕后实例化。为了理解这一点,让我们考虑一个简单的例子。
幕后无@JvmStatic
科特林代码
class Plant {
companion object {
fun waterAll() { }
}
}
反编译Java代码
public final class Plant {
public static final Plant.Companion Companion = new Plant.Companion();
public static final class Companion {
public final void waterAll() { }
private Companion() { }
}
}
正如您在上面的简化反编译Java代码中看到的,生成了一个名为Companion
的class来表示companion object
。 class Plant
包含 class Plant.Companion
的单例实例 new Plant.Companion()
。该实例也被命名为 Companion
。这就是您需要使用 Plant.Companion
:
companion object
的 functions/properties 的原因
Plant.Companion.waterAll();
在幕后与 @JvmStatic
科特林代码
class Plant {
companion object {
@JvmStatic
fun waterAll() { }
}
}
反编译Java代码
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() { }
}
}
当你在 Kotlin 中用 @JvmStatic
注释一个 companion object
的函数时,除了非静态函数 [=] 之外,还会生成一个纯 static
函数 waterAll()
33=]。因此,现在您可以在不使用 Companion
名称的情况下调用该函数,这更符合 Java:
Plant.waterAll();
单例
两种情况下都会生成单例模式。如您所见,在这两种情况下,Companion
实例都包含单例对象 new Plant.Companion()
并且构造函数 private
以防止多个实例。
Java static
关键字不创建单例。只有在 Kotlin 中创建 companion object
然后从 Java 中使用它,您才会获得单例功能。要从 Java 获取单例,您需要编写单例模式,其代码看起来像上面显示的反编译 Java 代码。
性能
在内存分配方面没有性能提升或损失。原因是,正如您在上面的代码中看到的,生成的额外 static
函数将其工作委托给非静态函数 Companion.waterAll()
。这意味着,在两种情况下都需要创建 Companion
实例,使用 @JvmStatic
以及不使用 @JvmStatic
.
除了生成的额外方法外,两种设置的行为相同。在Android中,如果您担心方法计数,您可能需要留意这一点,因为为每个注释函数创建了一个额外的副本。
何时使用 @JvmStatic
当您知道您的 Kotlin 代码不会在 Java 中使用时,您不必担心添加 @JvmStatic
注释。这使您的代码更清晰。但是,如果您的 Kotlin 代码是从 Java 调用的,则添加注释是有意义的。这将防止您的 Java 代码随处被名称 Companion
污染。
它不像任何一方的附加关键字。如果在一个地方添加 @JvmStatic
,则可以防止在数千个地方写入额外的 Companion
单词,无论您在何处调用该函数。这对库创建者特别有用,如果他们在他们的 Kotlin 库中添加 @JvmStatic
,该库的用户将不必在他们的 Java 代码中使用 Companion
词。
就是这样!希望这有助于更清楚地了解 @JvmStatic
.