Почему и когда использовать @JvmStatic с сопутствующими объектами?
Я пытаюсь понять разницу между использованием/не использованием @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?
Благодарю!
Ответы
Ответ 1
Поведение аннотации @JvmStatic
подробно объясняется в документации. Читая документацию, вы должны исходить из того, что она дает вам всю важную информацию, а различия в поведении, которые не упомянуты в документации, не существуют.
В этом случае в документации говорится:
Если вы используете эту аннотацию, компилятор сгенерирует как статический метод во включающем классе объекта, так и метод экземпляра в самом объекте.
Другими словами, эффект аннотации заключается в том, что она указывает компилятору генерировать дополнительный метод.
В документации упоминается, что есть какие-либо различия в поведении или распределении памяти? Это не. Поэтому можно с уверенностью предположить, что их нет.
Есть ли предпочтение, какое из них использовать? Обычно API объявляется в одном месте и используется из нескольких мест. Если вы вызываете метод из Java, вы должны объявить его как @JvmStatic
, потому что добавление аннотации @JvmStatic
в одном месте позволит вам пропустить несколько ссылок .Companion
в нескольких местах.
Создают ли оба псевдостатический одноэлементный объект, как в Java static? Этот вопрос не имеет смысла, потому что Java static не создает "псевдостатический одноэлементный объект". Если вы объявите статический метод в классе Java, а затем вызовете этот метод, никакие объекты не будут созданы.
Ответ 2
В Kotlin объект- companion
можно использовать для имитации статического поведения, вызовы выглядят как статические вызовы в Java, "Companion"
не является частью if. Если используется в Java, то companion
объект должен быть назван, если не применяется @JvmStatic
. В противном случае Itd выглядит менее идиоматично.
TestKotlin.getSomeString() //this should be preferred whenever possible
В документах:
Сопутствующие объекты
Объявление объекта внутри класса может быть помечено ключевым словом companion:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
Члены сопутствующего объекта можно вызвать, просто используя имя класса в качестве определителя:
val instance = MyClass.create()
...
Однако на JVM вы можете иметь членов сопутствующих объектов, сгенерированных как реальные статические методы и поля, если вы используете аннотацию @JvmStatic
. Подробнее см. Раздел "Интерфейс взаимодействия с Java".
Обратите внимание, что он будет генерировать дополнительный метод, как указано здесь:
Если вы используете эту аннотацию, компилятор будет генерировать как статический метод в охватывающем классе объекта, так и метод экземпляра в самом объекте.
Рассмотрим пример:
Следующий класс
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();
}
Ответ 3
Вы помещаете функцию в "объект-компаньон".
Таким образом, код java выглядит так:
class DemoClass {
public static int myMethod() { return 1; }
}
станет
class DemoClass {
companion object {
fun myMethod() : Int = 1
}
}
Затем вы можете использовать его из кода Котлина в качестве
DemoClass.myMethod();
Но из кода Java вам нужно будет назвать это как
DemoClass.Companion.myMethod();
(Который также работает из Котлина).
Если вам не нравится указывать бит Companion
вы можете либо добавить аннотацию @JvmStatic
либо назвать свой сопутствующий класс.
Из документов:
Сопутствующие объекты
Объявление объекта внутри класса может быть помечено ключевым словом companion:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
Члены сопутствующего объекта можно вызвать, просто используя имя класса в качестве определителя:
val instance = MyClass.create()
...
Однако на JVM вы можете иметь членов сопутствующих объектов, сгенерированных как реальные статические методы и поля, если вы используете аннотацию @JvmStatic
. Подробнее см. Раздел "Интерфейс взаимодействия с Java".
Добавление аннотации @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, как DemoClass.Blah.myMethod()
(который также будет работать в Котлине).