Звонок Котлин с Явы
Котлинский код можно легко вызвать с Java.Например,экземпляры класса Kotlin могут быть легко созданы и работать в методах Java.Тем не менее,существуют определенные различия между Java и Котлином,которые требуют внимания при интеграции кода Котлин в Java.На этой странице мы расскажем о том,как настроить взаимодействие вашего кода Котлин с его Java-клиентами.
Properties
Свойство Kotlin компилируется в следующие Java-элементы:
- метод получения, имя которого вычисляется путем добавления префикса get
- метод установки, имя которого вычисляется путем добавления префикса set (только для свойств var )
- частное поле,имя которого совпадает с именем свойства (только для свойств с обратными полями)
Например, var firstName: String компилируется в следующие объявления Java:
private String firstName; public String getFirstName( ) < return firstName; > public void setFirstName(String firstName) < this.firstName = firstName; >
Если имя свойства начинается с is , используется другое правило сопоставления имен: имя получателя будет таким же, как имя свойства, а имя установщика будет получено путем замены is на set . Например, для свойства isOpen метод получения будет вызываться isOpen() а метод установки будет называться setOpen() . Это правило применяется для свойств любого типа, а не только для Boolean .
Package-level functions
Все функции и свойства, объявленные в файле app.kt внутри пакета org.example , включая функции расширения, компилируются в статические методы класса Java с именем org.example.AppKt .
// app.kt package org.example class Util fun getTime() < /*. */ >
// Java new org.example.Util(); org.example.AppKt.getTime();
Чтобы установить собственное имя для сгенерированного класса Java, используйте аннотацию @JvmName :
@file:JvmName("DemoUtils") package org.example class Util fun getTime() < /*. */ >
// Java new org.example.Util(); org.example.DemoUtils.getTime();
Наличие нескольких файлов с одинаковым сгенерированным именем класса Java (один и тот же пакет и одно и то же имя или одна и та же аннотация @JvmName ) обычно является ошибкой. Однако компилятор может сгенерировать один фасадный класс Java с указанным именем и содержать все объявления из всех файлов с таким именем. Чтобы включить создание такого фасада, используйте аннотацию @JvmMultifileClass во всех таких файлах.
// oldutils.kt @file:JvmName("Utils") @file:JvmMultifileClass package org.example fun getTime() < /*. */ >
// newutils.kt @file:JvmName("Utils") @file:JvmMultifileClass package org.example fun getDate() < /*. */ >
// Java org.example.Utils.getTime(); org.example.Utils.getDate();
Instance fields
Если вам нужно представить свойство Kotlin как поле в Java, аннотируйте его аннотацией @JvmField . Поле будет иметь ту же видимость, что и базовое свойство. Вы можете аннотировать свойство с помощью @JvmField , если оно:
- имеет опорное поле
- не является частной
- не имеет модификаторов open , override или const
- не является делегированной собственностью
class User(id: String) < @JvmField val >// Java class JavaClient < public String getID(User user) < return user.ID; > >
Свойства с поздней инициализацией также отображаются как поля. Видимость поля будет такой же, как видимость установщика свойства lateinit .
Static fields
Свойства Kotlin,объявленные в именованном объекте или в качестве объекта-сопутника,будут иметь статические поля подложки либо в этом именованном объекте,либо в классе,содержащем объект-сопутник.
Обычно эти поля являются приватными,но они могут быть открыты одним из следующих способов:
@JvmField такого свойства с помощью @JvmField делает его статическим полем с такой же видимостью, как и само свойство.
class Key(val value: Int) < companion object < @JvmField val COMPARATOR: Comparator = compareBy < it.value >> >
// Java Key.COMPARATOR.compare(key1, key2); // публичное статическое конечное поле в классе Key
Поздно инициализированное свойство в объекте или сопутствующем объекте имеет статическое резервное поле с той же видимостью, что и средство установки свойства.
object Singleton < lateinit var provider: Provider >
// Java Singleton.provider = new Provider(); // публичное статическое незавершенное поле в классе Singleton
Свойства, объявленные как const (как в классах, так и на верхнем уровне), в Java превращаются в статические поля:
// файл example.kt object Obj < const val CONST = 1 > class C < companion object < const val VERSION = 9 > > const val MAX = 239
int constant = Obj.CONST; int max = ExampleKt.MAX; int version = C.VERSION;
Static methods
Как упоминалось выше, Kotlin представляет функции уровня пакета как статические методы. Kotlin также может генерировать статические методы для функций, определенных в именованных объектах или сопутствующих объектах, если вы аннотируете эти функции как @JvmStatic . Если вы используете эту аннотацию, компилятор сгенерирует как статический метод в охватывающем классе объекта, так и метод экземпляра в самом объекте. Например:
class C < companion object < @JvmStatic fun callStatic() <> fun callNonStatic() <> > >
Теперь callStatic() статичен в Java, а callNonStatic() нет:
C.callStatic(); // работает отлично C.callNonStatic(); // ошибка: не статический метод C.Companion.callStatic(); // метод экземпляра остается C.Companion.callNonStatic(); // единственный способ работы
То же самое для именованных объектов:
object Obj < @JvmStatic fun callStatic() <> fun callNonStatic() <> >
Obj.callStatic(); // работает отлично Obj.callNonStatic(); // error Obj.INSTANCE.callNonStatic(); // работает, вызов через экземпляр singleton Obj.INSTANCE.callStatic(); // тоже работает
Начиная с Kotlin 1.3, @JvmStatic применяется также к функциям, определенным в сопутствующих объектах интерфейсов. Такие функции компилируются в статические методы в интерфейсах. Обратите внимание, что статический метод в интерфейсах был введен в Java 1.8, поэтому обязательно используйте соответствующие цели.
interface ChatBot < companion object < @JvmStatic fun greet(username: String) < println("Hello, $username") > > >
@JvmStatic Аннотация @JvmStatic также может применяться к свойству объекта или сопутствующего объекта, делая его методы получения и установки статическими членами в этом объекте или классе, содержащем сопутствующий объект.
Методы по умолчанию в интерфейсах
Методы по умолчанию доступны только для целей JVM 1.8 и выше.
Начиная с JDK 1.8, интерфейсы в Java могут содержать методы по умолчанию . Чтобы сделать все неабстрактные члены интерфейсов Kotlin используемыми по умолчанию для реализующих их классов Java, скомпилируйте код Kotlin с параметром компилятора -Xjvm-default=all .
Вот пример интерфейса Kotlin с методом по умолчанию:
// компилировать с -Xjvm-default = all interface Robot < fun move() < println("~walking~") > // будет по умолчанию в интерфейсе Java fun speak(): Unit >
Реализация по умолчанию доступна для классов Java,реализующих интерфейс.
//Java implementation public class C3PO implements Robot < // реализация move () из робота доступна неявно @Override public void speak( ) < System.out.println("I beg your pardon, sir"); > >
C3PO c3po = new C3PO(); c3po.move(); // реализация по умолчанию из интерфейса робота c3po.speak();
Введение интерфейса может переопределить методы по умолчанию.
//Java public class BB8 implements Robot < // собственная реализация метода по умолчанию @Override public void move( ) < System.out.println("~rolling~"); > @Override public void speak( ) < System.out.println("Beep-beep"); > >
До Kotlin 1.4 для создания методов по умолчанию можно было использовать аннотацию @JvmDefault для этих методов. Компиляция с -Xjvm-default=all в 1.4+ обычно работает так, как если бы вы аннотировали все неабстрактные методы интерфейсов с помощью @JvmDefault и скомпилировали с -Xjvm-default=enable . Однако бывают случаи, когда их поведение отличается. Подробная информация об изменениях в генерации методов по умолчанию в Kotlin 1.4 представлена в этом посте в блоге Kotlin.
Режимы совместимости для методов по умолчанию
Если есть клиенты, которые используют ваши интерфейсы Kotlin, скомпилированные без опции -Xjvm-default=all , то они могут быть двоично-несовместимы с кодом, скомпилированным с этой опцией. Чтобы не нарушать совместимость с такими клиентами, используйте режим -Xjvm-default=all и помечайте интерфейсы аннотацией @JvmDefaultWithCompatibility . Это позволяет вам один раз добавить эту аннотацию ко всем интерфейсам в общедоступном API, и вам не нужно будет использовать какие-либо аннотации для нового непубличного кода.
Начиная с Kotlin 1.6.20, вы можете компилировать модули в режиме по умолчанию ( опция компилятора -Xjvm-default=disable ) против модулей, скомпилированных с -Xjvm-default=all или -Xjvm-default=all-compatibility .
Узнайте больше о режимах совместимости:
disable
Поведение по умолчанию. Не генерируйте методы JVM по умолчанию и @JvmDefault использование аннотаций @JvmDefault.
all
Создайте методы JVM по умолчанию для всех объявлений интерфейса с телами в модуле. Не генерируйте заглушки DefaultImpls для объявлений интерфейса с телами, которые генерируются по умолчанию в disable режиме.
Если интерфейс наследует метод с телом от интерфейса, скомпилированного в disable режиме, и не переопределяет его, то для него будет сгенерирована заглушка DefaultImpls .
Нарушает двоичную совместимость , если какой-либо клиентский код зависит от наличия классов DefaultImpls .
Если используется делегирование интерфейса, делегируются все методы интерфейса. Единственным исключением являются методы, помеченные устаревшей аннотацией @JvmDefault .
all-compatibility
В дополнение к режиму all создайте заглушки совместимости в классах DefaultImpls . Заглушки совместимости могут быть полезны для авторов библиотек и среды выполнения, чтобы поддерживать обратную двоичную совместимость для существующих клиентов, скомпилированных с предыдущими версиями библиотек. all и all-compatibility изменяют поверхность библиотеки ABI, которую клиенты будут использовать после перекомпиляции библиотеки. В этом смысле клиенты могут быть несовместимы с предыдущими версиями библиотек. Обычно это означает, что вам нужна правильная версия библиотеки, например, увеличение основной версии в SemVer.
Компилятор генерирует все члены DefaultImpls с аннотацией @Deprecated : вы не должны использовать эти члены в коде Java, потому что компилятор создает их только в целях совместимости.
В случае наследования от интерфейса Kotlin, скомпилированного в режимах полной или полной совместимости, all совместимости all-compatibility DefaultImpls вызывать метод интерфейса по умолчанию со стандартной семантикой разрешения времени выполнения JVM.
disable режиме генерировался дополнительный неявный метод со специализированными сигнатурами : в отличие от disable режима, компилятор сообщит об ошибке, если вы не переопределите такой метод явно и не не аннотировать класс @JvmDefaultWithoutCompatibility ( подробности см. в этом выпуске YouTrack ).
© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/java-to-kotlin-interop.html
Kotlin 1.8
В Java для сравнения объектов по нескольким критериям можно использовать функции compare() и thenComparingX() из интерфейса Comparator.
Модификаторы видимости Kotlin сопоставляются с Java следующим образом: частные члены скомпилированы в частные объявления верхнего уровня, скомпилированы на уровне пакета, защищены