Spec-Zone.ru › Kotlin 1.9

Вызов Java из Kotlin

Kotlin разработан с учётом межплатформенности с Java. Существующий код Java может вызываться из Kotlin естественным образом, и код Kotlin может использоваться из Java также без особых проблем. В этом разделе мы опишем некоторые детали вызова кода Java из Kotlin.

Практически весь код Java может использоваться без каких-либо проблем:

import java.util.*

fun demo(source: List<Int>) {
    val list = ArrayList<Int>()
    // 'for'-loops work for Java collections:
    for (item in source) {
        list.add(item)
    }
    // Operator conventions work as well:
    for (i in 0..source.size - 1) {
        list[i] = source[i] // get and set are called
    }
}

Геттеры и сеттеры

Методы, которые следуют соглашениям Java для геттеров и сеттеров (методы без аргументов, имена которых начинаются с get , и одноаргументные методы, имена которых начинаются с set ), представляются в Kotlin как свойства. Такие свойства также называются синтетическими свойствами. Boolean методы доступа (где имя геттера начинается с is , а имя сеттера начинается с set ) представляются как свойства, имеющие то же имя, что и метод геттера.

import java.util.Calendar

fun calendarDemo() {
    val calendar = Calendar.getInstance()
    if (calendar.firstDayOfWeek == Calendar.SUNDAY) { // call getFirstDayOfWeek()
        calendar.firstDayOfWeek = Calendar.MONDAY // call setFirstDayOfWeek()
    }
    if (!calendar.isLenient) { // call isLenient()
        calendar.isLenient = true // call setLenient()
    }
}

calendar.firstDayOfWeek выше является примером синтетического свойства.

Обратите внимание, что если в классе Java есть только сеттер, он не отображается как свойство в Kotlin, поскольку Kotlin не поддерживает свойства только для записи.

Ссылки на синтетические свойства Java

Эта функция является Экспериментальной. Она может быть удалена или изменена в любое время. Мы рекомендуем использовать её только для оценки.

Начиная с Kotlin 1.8.20, вы можете создавать ссылки на синтетические свойства Java. Рассмотрим следующий код Java:

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

Kotlin всегда позволял вам писать person.age, где age является синтетическим свойством. Теперь вы также можете создавать ссылки на Person::age и person::age. То же самое относится к name, тоже.

val persons = listOf(Person("Jack", 11), Person("Sofie", 12), Person("Peter", 11))
    Persons
         // Call a reference to Java synthetic property:
        .sortedBy(Person::age)
         // Call Java getter via the Kotlin property syntax:
        .forEach { person -> println(person.name) }
}

Как включить ссылки на синтетические свойства Java

Чтобы включить эту функцию, установите опцию компилятора -language-version 2.1. В проекте Gradle вы можете сделать это, добавив следующее в свой build.gradle(.kts).

tasks
    .withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
    .configureEach {
        compilerOptions
            .languageVersion
            .set(
                org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_1
            )
    }
tasks
    .withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
    .configureEach {
        compilerOptions.languageVersion
            = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_1
}

До Kotlin 1.9.0 для включения этой функции нужно было установить опцию компилятора -language-version 1.9.

Методы, возвращающие void

Если метод Java возвращает void, он вернёт Unit при вызове из Kotlin. Если кто-то по какой-то причине использует это возвращаемое значение, оно будет присвоено в месте вызова компилятором Kotlin, так как само значение известно заранее (будучи Unit).

Экранирование для идентификаторов Java, являющихся ключевыми словами в Kotlin

Некоторые ключевые слова Kotlin являются допустимыми идентификаторами в Java: in, object, is, и другие. Если Java-библиотека использует ключевое слово Kotlin для метода, вы всё равно можете вызвать метод, экранируя его с помощью символа обратной кавычки (`):

foo.`is`(bar)

Безопасность от null и типы платформы

Любая ссылка в Java может быть null, что делает требования Kotlin по строгой безопасности от null непрактичными для объектов, полученных из Java. Типы объявлений Java обрабатываются в Kotlin особым образом и называются типами платформы. Проверки на null ослаблены для таких типов, поэтому гарантии безопасности для них такие же, как и в Java (см. более подробную информацию ниже).

Рассмотрим следующие примеры:

val list = ArrayList<String>() // non-null (constructor result)
list.add("Item")
val size = list.size // non-null (primitive int)
val item = list[0] // platform type inferred (ordinary Java object)

При вызове методов на переменных типов платформы Kotlin не выдает ошибок нулевости во время компиляции, но вызов может завершиться ошибкой во время выполнения из-за исключения null-указателя или утверждения, которое Kotlin генерирует для предотвращения распространения null:

item.substring(1) // allowed, throws an exception if item == null

Типы платформы являются неотображаемыми, что означает, что вы не можете записать их явно в языке. Когда платформа значение присваивается переменной Kotlin, вы можете положиться на вывод типа (переменная тогда будет иметь выведенный тип платформы, как item в примере выше), или вы можете выбрать ожидаемый тип (разрешены как nullable, так и non-null типы):

val nullable: String? = item // allowed, always works
val notNull: String = item // allowed, may fail at runtime

Если вы выберете тип non-null, компилятор сгенерирует утверждение при присваивании. Это предотвращает хранение null в переменных Kotlin non-null. Утверждения также генерируются, когда вы передаете значения платформы в функции Kotlin, ожидающие non-null значения, и в других случаях. В целом, компилятор делает все возможное, чтобы предотвратить распространение null по программе, хотя иногда это невозможно полностью устранить из-за дженериков.

Нотация для типов платформы

Как упоминалось выше, типы платформы не могут быть упомянуты явно в программе, поэтому в языке нет синтаксиса для них. Тем не менее, компилятору и IDE иногда необходимо отображать их (например, в сообщениях об ошибках или информации о параметрах), поэтому для них существует мнемоническая нотация:

  • T! означает "T или T?",

  • (Mutable)Collection<T>! означает "коллекция Java типа T может быть изменяемой или неизменяемой, может быть nullable или non-null",

  • Array<(out) T>! означает "массив Java типа T (или подтип T), nullable или non-null"

Аннотации нулевости

Типы Java, имеющие аннотации нулевости, представлены не как типы платформы, а как фактические nullable или non-null типы Kotlin. Компилятор поддерживает несколько вариантов аннотаций нулевости, включая:

  • JetBrains (@Nullable и @NotNull из пакета org.jetbrains.annotations)

  • JSpecify (org.jspecify.nullness)

  • Android (com.android.annotations и android.support.annotations)

  • JSR-305 (javax.annotation, более подробная информация ниже)

  • FindBugs (edu.umd.cs.findbugs.annotations)

  • Eclipse (org.eclipse.jdt.annotation)

  • Lombok (lombok.NonNull)

  • RxJava 3 (io.reactivex.rxjava3.annotations)

Вы можете указать, должен ли компилятор сообщать о несоответствии нулевости на основе информации из определенных типов аннотаций нулевости. Используйте опцию компилятора -Xnullability-annotations=@<package-name>:<report-level>. В аргументе укажите полное имя пакета аннотаций нулевости и один из уровней отчёта:

  • ignore для игнорирования несоответствий нулевости

  • warn для отчета о предупреждениях

  • strict для отчета об ошибках.

Полный список поддерживаемых аннотаций нулевости см. в исходном коде компилятора Kotlin.

Аннотирование аргументов и параметров типов

Вы можете аннотировать аргументы и параметры типов дженериков, чтобы предоставить информацию о нулевости для них тоже.

Все примеры в разделе используют аннотации нулевости JetBrains из пакета org.jetbrains.annotations.

Аргументы типов

Рассмотрим эти аннотации в объявлении Java:

@NotNull
Set<@NotNull String> toSet(@NotNull Collection<@NotNull String> elements) { ... }

Они приводят к следующей сигнатуре в Kotlin:

fun toSet(elements: (Mutable)Collection<String>) : (Mutable)Set<String> { ... }

Если аннотация @NotNull отсутствует у аргумента типа, вместо него используется тип платформы:

fun toSet(elements: (Mutable)Collection<String!>) : (Mutable)Set<String!> { ... }

Kotlin также учитывает аннотации нулевости в аргументах типов базовых классов и интерфейсов. Например, есть два класса Java со следующими сигнатурами:

public class Base<T> {}
public class Derived extends Base<@Nullable String> {}

В Kotlin-коде передача экземпляра Derived, где предполагается, что Base<String>, порождает предупреждение.

fun takeBaseOfNotNullStrings(x: Base<String>) {}

fun main() {
    takeBaseOfNotNullStrings(Derived()) // warning: nullability mismatch
}

Верхняя граница Derived установлена в Base<String?>, что отличается от Base<String>.

Подробнее о Java дженериках в Kotlin.

Параметры типов

По умолчанию нулевость простых параметров типов в Kotlin и Java не определена. В Java вы можете указать ее с помощью аннотаций нулевости. Давайте аннотируем параметр типа класса Base:

public class Base<@NotNull T> {}

При наследовании от Base, Kotlin ожидает non-null аргумент или параметр типа. Таким образом, следующий Kotlin-код генерирует предупреждение:

class Derived<K> : Base<K> {} // warning: K has undefined nullability

Вы можете исправить это, указав верхнюю границу K : Any.

Kotlin также поддерживает аннотации нулевости в границах параметров типов Java. Давайте добавим границы для Base:

public class BaseWithBound<T extends @NotNull Number> {}

Kotlin преобразует это следующим образом:

class BaseWithBound<T : Number> {}

Таким образом, передача nullable типа как аргумента или параметра типа приводит к предупреждению.

Аннотирование аргументов и параметров типов работает с целевой версией Java 8 или выше. Для этой функции требуется, чтобы аннотации нулевости поддерживали целевую версию TYPE_USE. (org.jetbrains.annotations поддерживает это начиная с версии 15 и выше). Используйте опцию компилятора -Xtype-enhancement-improvements-strict-mode для отчёта об ошибках в Kotlin-коде, использующем нулевость, которая отличается от аннотаций нулевости из Java.

Примечание: Если аннотация нулевости поддерживает другие целевые версии, применимые к типу помимо TYPE_USE, тогда TYPE_USE, имеет приоритет. Например, если @Nullable имеет как TYPE_USE, так и METHOD целевые версии, сигнатура Java-метода @Nullable String[] f() станет fun f(): Array<String?>! в Kotlin.

Поддержка JSR-305

Аннотация @Nonnull, определённая в JSR-305, поддерживается для обозначения возможности значения null для типов Java.

Если значение @Nonnull(when = ...) равно When.ALWAYS, аннотированный тип рассматривается как не допускающий значение null; When.MAYBE и When.NEVER обозначают тип, допускающий значение null; и When.UNKNOWN принудительно устанавливает тип в платформенный.

Библиотека может быть скомпилирована с аннотациями JSR-305, но нет необходимости делать аннотации зависимостью от компиляции для потребителей библиотеки (например, jsr305.jar). Компилятор Kotlin может читать аннотации JSR-305 из библиотеки без присутствия аннотаций в пути к классам.

Настраиваемые квалификаторы применимости null (KEEP-79) также поддерживаются (см. ниже).

Псевдонимы квалификаторов типов

Если тип аннотации аннотирован как @TypeQualifierNickname и JSR-305 @Nonnull (или его другим псевдонимом, таким как @CheckForNull), то тип аннотации сам используется для получения точной информации о возможности значения null и имеет тот же смысл, что и аннотация применимости null:

@TypeQualifierNickname
@Nonnull(when = When.ALWAYS)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyNonnull {
}

@TypeQualifierNickname
@CheckForNull // a nickname to another type qualifier nickname
@Retention(RetentionPolicy.RUNTIME)
public @interface MyNullable {
}

interface A {
    @MyNullable String foo(@MyNonnull String x);
    // in Kotlin (strict mode): `fun foo(x: String): String?`

    String bar(List<@MyNonnull String> x);
    // in Kotlin (strict mode): `fun bar(x: List<String>!): String!`
}

Значения по умолчанию квалификаторов типов

@TypeQualifierDefault позволяет вводить аннотации, которые при применении определяют значение по умолчанию для возможности значения null в рамках аннотированного элемента.

Такой тип аннотации сам должен быть аннотирован как @Nonnull (или его псевдонимом) и @TypeQualifierDefault(...) с одним или несколькими значениями ElementType:

  • ElementType.METHOD для возвращаемых типов методов

  • ElementType.PARAMETER для параметров значений

  • ElementType.FIELD для полей

  • ElementType.TYPE_USE для любого типа, включая аргументы типов, верхние границы параметров типов и типы с подстановочными знаками

Значение по умолчанию для возможности значения null используется, когда тип сам по себе не аннотирован аннотацией применимости null, и значение по умолчанию определяется самым внутренним окружающим элементом, аннотированным аннотацией значения по умолчанию для квалификатора типа с ElementType соответствием использованию типа.

@Nonnull
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
public @interface NonNullApi {
}

@Nonnull(when = When.MAYBE)
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE_USE})
public @interface NullableApi {
}

@NullableApi
interface A {
    String foo(String x); // fun foo(x: String?): String?

    @NotNullApi // overriding default from the interface
    String bar(String x, @Nullable String y); // fun bar(x: String, y: String?): String

    // The List<String> type argument is seen as nullable because of `@NullableApi`
    // having the `TYPE_USE` element type:
    String baz(List<String> x); // fun baz(List<String?>?): String?

    // The type of `x` parameter remains platform because there's an explicit
    // UNKNOWN-marked nullability annotation:
    String qux(@Nonnull(when = When.UNKNOWN) String x); // fun baz(x: String!): String?
}

Типы в этом примере появляются только при включенном строгом режиме; в противном случае типы платформы остаются. См. раздел @UnderMigration аннотация и Конфигурация компилятора.

Также поддерживается значение по умолчанию для возможности значения null на уровне пакета:

// FILE: test/package-info.java
@NonNullApi // declaring all types in package 'test' as non-nullable by default
package test;

Аннотация @UnderMigration

Аннотация @UnderMigration (предоставленная в отдельном артефакте kotlin-annotations-jvm) может использоваться разработчиками библиотек для определения статуса миграции для квалификаторов типов применимости null.

Значение статуса в @UnderMigration(status = ...) определяет, как компилятор обрабатывает ненадлежащие использования аннотированных типов в Kotlin (например, использование значения типа, аннотированного @MyNullable, как не допускающего null):

  • MigrationStatus.STRICT делает аннотацию работающей как любая простая аннотация применимости null, т.е. сообщает об ошибках для ненадлежащих использований и влияет на типы в аннотированных объявлениях так, как они видны в Kotlin

  • MigrationStatus.WARN: ненадлежащие использования сообщаются как предупреждения при компиляции вместо ошибок, но типы в аннотированных объявлениях остаются платформами

  • MigrationStatus.IGNORE заставляет компилятор полностью игнорировать аннотацию применимости null

Разработчик библиотеки может добавить статус @UnderMigration к псевдонимам квалификаторов типов и значениям по умолчанию для квалификаторов типов:

@Nonnull(when = When.ALWAYS)
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
@UnderMigration(status = MigrationStatus.WARN)
public @interface NonNullApi {
}

// The types in the class are non-null, but only warnings are reported
// because `@NonNullApi` is annotated `@UnderMigration(status = MigrationStatus.WARN)`
@NonNullApi
public class Test {}

Статус миграции аннотации применимости null не наследуется его псевдонимами квалификаторов типов, а применяется к её использованиям в квалификаторах типа по умолчанию.

Если квалификатор типа по умолчанию использует псевдоним квалификатора типа, и они оба @UnderMigration, используется статус от квалификатора типа по умолчанию.

Конфигурация компилятора

Проверки JSR-305 могут быть настроены путём добавления флага компилятора -Xjsr305 со следующими параметрами (и их комбинацией):

  • -Xjsr305={strict|warn|ignore} для настройки поведения для аннотаций, не являющихся @UnderMigration. Настраиваемые квалификаторы применимости null, особенно @TypeQualifierDefault, уже распространены во многих известных библиотеках, и пользователям может потребоваться плавная миграция при обновлении до версии Kotlin, содержащей поддержку JSR-305. Начиная с Kotlin 1.1.60, этот флаг влияет только на аннотации, не являющиеся @UnderMigration.

  • -Xjsr305=under-migration:{strict|warn|ignore} для переопределения поведения аннотаций @UnderMigration. У пользователей может быть разное представление о статусе миграции для библиотек: они могут захотеть иметь ошибки, в то время как официальный статус миграции WARN, или наоборот, они могут пожелать отложить сообщения об ошибках для некоторых до завершения миграции.

  • -Xjsr305=@<fq.name>:{strict|warn|ignore} для переопределения поведения для одной аннотации, где <fq.name> — полное имя класса аннотации. Может появляться несколько раз для разных аннотаций. Это полезно для управления состоянием миграции для конкретной библиотеки.

Значения strict, warn и ignore имеют тот же смысл, что и значения MigrationStatus, и только режим strict влияет на типы в аннотированных объявлениях так, как они видны в Kotlin.

Примечание: встроенные аннотации JSR-305 @Nonnull, @Nullable и @CheckForNull всегда включены и влияют на типы аннотированных объявлений в Kotlin независимо от конфигурации компилятора с флагом -Xjsr305.

Например, добавление -Xjsr305=ignore -Xjsr305=under-migration:ignore -Xjsr305=@org.library.MyNullable:warn в аргументы компилятора заставляет компилятор генерировать предупреждения о ненадлежащих использованиях типов, аннотированных @org.library.MyNullable, и игнорировать все остальные аннотации JSR-305.

Поведение по умолчанию такое же, как у -Xjsr305=warn. Значение strict следует считать экспериментальным (в будущем к нему могут быть добавлены дополнительные проверки).

Сопоставленные типы

Kotlin обрабатывает некоторые типы Java особым образом. Такие типы не загружаются из Java "как есть", а сопоставляются с соответствующими типами Kotlin. Сопоставление имеет значение только во время компиляции, представление во время выполнения остается неизменным. Примитивные типы Java сопоставляются с соответствующими типами Kotlin (учитывая платформенные типы):

Тип Java

Тип Kotlin

byte

kotlin.Byte

short

kotlin.Short

int

kotlin.Int

long

kotlin.Long

char

kotlin.Char

float

kotlin.Float

double

kotlin.Double

boolean

kotlin.Boolean

Также сопоставляются некоторые встроенные классы, не являющиеся примитивными:

Тип Java

Тип Kotlin

java.lang.Object

kotlin.Any!

java.lang.Cloneable

kotlin.Cloneable!

java.lang.Comparable

kotlin.Comparable!

java.lang.Enum

kotlin.Enum!

java.lang.annotation.Annotation

kotlin.Annotation!

java.lang.CharSequence

kotlin.CharSequence!

java.lang.String

kotlin.String!

java.lang.Number

kotlin.Number!

java.lang.Throwable

kotlin.Throwable!

Упакованные примитивные типы Java сопоставляются с допускающими значение null типами Kotlin:

Тип Java

Тип Kotlin

java.lang.Byte

kotlin.Byte?

java.lang.Short

kotlin.Short?

java.lang.Integer

kotlin.Int?

java.lang.Long

kotlin.Long?

java.lang.Character

kotlin.Char?

java.lang.Float

kotlin.Float?

java.lang.Double

kotlin.Double?

java.lang.Boolean

kotlin.Boolean?

Обратите внимание, что упакованный примитивный тип, используемый в качестве параметра типа, сопоставляется с платформенным типом: например, List<java.lang.Integer> становится List<Int!> в Kotlin.

Типы коллекций могут быть только для чтения или изменяемыми в Kotlin, поэтому коллекции Java сопоставляются следующим образом (все типы Kotlin в этой таблице находятся в пакете kotlin.collections):

Тип Java

Тип Kotlin только для чтения

Изменяемый тип Kotlin

Загруженный платформенный тип

Iterator<T>

Iterator<T>

MutableIterator<T>

(Mutable)Iterator<T>!

Iterable<T>

Iterable<T>

MutableIterable<T>

(Mutable)Iterable<T>!

Collection<T>

Collection<T>

MutableCollection<T>

(Mutable)Collection<T>!

Set<T>

Set<T>

MutableSet<T>

(Mutable)Set<T>!

List<T>

List<T>

MutableList<T>

(Mutable)List<T>!

ListIterator<T>

ListIterator<T>

MutableListIterator<T>

(Mutable)ListIterator<T>!

Map<K, V>

Map<K, V>

MutableMap<K, V>

(Mutable)Map<K, V>!

Map.Entry<K, V>

Map.Entry<K, V>

MutableMap.MutableEntry<K,V>

(Mutable)Map.(Mutable)Entry<K, V>!

Массивы Java сопоставляются, как указано ниже:

Тип Java

Тип Kotlin

int[]

kotlin.IntArray!

String[]

kotlin.Array<(out) String>!

Статические члены этих типов Java не доступны напрямую для объектов-компаньонов типов Kotlin. Чтобы вызвать их, используйте полные квалифицированные имена типов Java, например java.lang.Integer.toHexString(foo).

Обобщения Java в Kotlin

Обобщения Kotlin немного отличаются от обобщений Java (см. Обобщения). При импорте типов Java в Kotlin выполняются следующие преобразования:

  • Подстановочные знаки Java преобразуются в проекции типов:

    • Foo<? extends Bar> становится Foo<out Bar!>!

    • Foo<? super Bar> становится Foo<in Bar!>!

  • Необработанные типы Java преобразуются в проекции со звездочкой:

    • List становится List<*>!, что является List<out Any?>!

Как и обобщения Java, обобщения Kotlin не сохраняются во время выполнения: объекты не содержат информацию о фактических аргументах типа, переданных в их конструкторы. Например, ArrayList<Integer>() неотличим от ArrayList<Character>(). Это делает невозможным выполнение проверок is, учитывающих обобщения. Kotlin разрешает только проверки is для типов обобщений с проекцией со звездочкой:

if (a is List<Int>) // Error: cannot check if it is really a List of Ints
// but
if (a is List<*>) // OK: no guarantees about the contents of the list

Массивы Java

Массивы в Kotlin не являются инвариантными, в отличие от Java. Это означает, что Kotlin не позволит вам присвоить Array<String> массиву Array<Any>, что предотвращает возможную ошибку во время выполнения. Передача массива подкласса в качестве массива суперкласса в метод Kotlin также запрещена, но для методов Java это разрешено через платформенные типы в формате Array<(out) String>!.

Массивы используются с примитивными типами данных на платформе Java, чтобы избежать затрат на операции boxing/unboxing. Поскольку Kotlin скрывает эти детали реализации, для взаимодействия с кодом Java требуется обходной путь. Существуют специализированные классы для каждого типа примитивного массива (IntArray, DoubleArray, CharArray, и так далее), чтобы обрабатывать этот случай. Они не связаны с классом Array и компилируются в примитивные массивы Java для максимальной производительности.

Предположим, есть метод Java, который принимает массив целых чисел индексов:

public class JavaArrayExample {
    public void removeIndices(int[] indices) {
        // code here...
    }
}

Чтобы передать массив примитивных значений, вы можете сделать следующее в Kotlin:

val javaObj = JavaArrayExample()
val array = intArrayOf(0, 1, 2, 3)
javaObj.removeIndices(array)  // passes int[] to method

При компиляции в байт-код JVM компилятор оптимизирует доступ к массивам, так что нет накладных расходов:

val array = arrayOf(1, 2, 3, 4)
array[1] = array[1] * 2 // no actual calls to get() and set() generated
for (x in array) { // no iterator created
    print(x)
}

Даже при навигации с помощью индекса это не вносит никаких накладных расходов:

for (i in array.indices) { // no iterator created
    array[i] += 2
}

Наконец, in-проверки также не имеют накладных расходов:

if (i in array.indices) { // same as (i >= 0 && i < array.size)
    print(array[i])
}

Java varargs

Классы Java иногда используют объявление метода для индексов с переменным количеством аргументов (varargs):

public class JavaArrayExample {

    public void removeIndicesVarArg(int... indices) {
        // code here...
    }
}

В этом случае вам необходимо использовать оператор расширения * для передачи IntArray:

val javaObj = JavaArrayExample()
val array = intArrayOf(0, 1, 2, 3)
javaObj.removeIndicesVarArg(*array)

Операторы

Поскольку Java не имеет способа маркировать методы, для которых имеет смысл использовать синтаксис оператора, Kotlin позволяет использовать любые методы Java с правильным именем и сигнатурой в качестве перегрузок оператора и других соглашений (invoke() и т. д.). Вызов методов Java с помощью синтаксиса вызова инфикс запрещен.

Проверяемые исключения

В Kotlin все исключения не проверяемые, что означает, что компилятор не заставляет вас перехватывать какие-либо из них. Таким образом, когда вы вызываете метод Java, который объявляет проверяемое исключение, Kotlin не заставляет вас ничего делать:

fun render(list: List<*>, to: Appendable) {
    for (item in list) {
        to.append(item.toString()) // Java would require us to catch IOException here
    }
}

Методы объектов

При импорте типов Java в Kotlin все ссылки на тип java.lang.Object преобразуются в Any. Поскольку Any не является платформенно-специфическим, он объявляет только toString(), hashCode() и equals() в качестве своих членов, поэтому для того, чтобы сделать доступными другие члены java.lang.Object, Kotlin использует расширяющие функции.

wait()/notify()

Методы wait() и notify() недоступны для ссылок на тип Any. Их использование обычно не рекомендуется в пользу java.util.concurrent. Если вам действительно нужно вызвать эти методы, вы можете выполнить приведение к типу java.lang.Object:

(foo as java.lang.Object).wait()

getClass()

Для получения класса Java объекта используйте расширяющее свойство java для ссылки на класс:

val fooClass = foo::class.java

В приведенном выше коде используется связанная ссылка на класс. Также можно использовать расширяющее свойство javaClass:

val fooClass = foo.javaClass

clone()

Чтобы переопределить clone(), ваш класс должен расширять kotlin.Cloneable:

class Example : Cloneable {
    override fun clone(): Any { ... }
}

Не забывайте о Effective Java, 3-е издание, пункт 13: Переопределение clone обоснованно.

finalize()

Чтобы переопределить finalize(), вам просто нужно объявить его, не используя ключевое слово override:

class C {
    protected fun finalize() {
        // finalization logic
    }
}

Согласно правилам Java, finalize() не должен быть private.

Наследование от Java-классов

Максимум один класс Java (и любое количество интерфейсов Java) может быть супертипом для класса в Kotlin.

Доступ к статическим членам

Статические члены классов Java образуют «компаньоны объектов» для этих классов. Вы не можете передать такой «компаньон объекта» как значение, но можете получить доступ к членам явно, например:

if (Character.isLetter(a)) { ... }

Чтобы получить доступ к статическим членам типа Java, который отображается на тип Kotlin, используйте полное квалифицированное имя типа Java: java.lang.Integer.bitCount(foo).

Рефлексия Java

Рефлексия Java работает с классами Kotlin и наоборот. Как упоминалось выше, вы можете использовать instance::class.java, ClassName::class.java или instance.javaClass для входа в рефлексию Java через java.lang.Class. Не используйте ClassName.javaClass для этой цели, поскольку она относится к классу компаньона объекта ClassName, который идентичен ClassName.Companion::class.java, а не ClassName::class.java.

Для каждого примитивного типа существуют два различных класса Java, и Kotlin предоставляет способы получения обоих. Например, Int::class.java вернет экземпляр класса, представляющего сам примитивный тип, соответствующий Integer.TYPE в Java. Чтобы получить класс соответствующего оберточного типа, используйте Int::class.javaObjectType, что эквивалентно Integer.class в Java.

Другие поддерживаемые случаи включают получение метода getter/setter Java или базового поля для свойства Kotlin, KProperty для поля Java, метода или конструктора Java для KFunction и наоборот.

Преобразования SAM

Kotlin поддерживает преобразования SAM для интерфейсов Java и Kotlin. Эта поддержка Java означает, что литералы функций Kotlin могут быть автоматически преобразованы в реализации интерфейсов Java с одним методом без по умолчанию, при условии, что типы параметров метода интерфейса совпадают с типами параметров Kotlin-функции.

Вы можете использовать это для создания экземпляров интерфейсов SAM:

val runnable = Runnable { println("This runs in a runnable") }

...и в вызовах методов:

val executor = ThreadPoolExecutor()
// Java signature: void execute(Runnable command)
executor.execute { println("This runs in a thread pool") }

Если класс Java имеет несколько методов, принимающих функциональные интерфейсы, вы можете выбрать нужный метод, используя адаптерную функцию, которая преобразует лямбда-выражение в определенный тип SAM. Эти адаптерные функции также генерируются компилятором при необходимости:

executor.execute(Runnable { println("This runs in a thread pool") })

Преобразования SAM работают только для интерфейсов, а не для абстрактных классов, даже если они также имеют только один абстрактный метод.

Использование JNI с Kotlin

Чтобы объявить функцию, реализованную в коде на языке С или С++, необходимо пометить ее модификатором external:

external fun foo(x: Int): Double

Остальная часть процедуры работает точно так же, как и в Java.

Вы также можете пометить getter и setter свойств как external:

var myProperty: String
    external get
    external set

За кулисами это создаст две функции getMyProperty и setMyProperty, обе помеченные как external.

Использование Lombok-сгенерированных объявлений в Kotlin

Вы можете использовать сгенерированные Lombok объявления Java в коде Kotlin. Если вам нужно сгенерировать и использовать эти объявления в одном смешанном модуле Java/Kotlin, вы можете узнать, как это сделать на странице плагина-компилятора Lombok Lombok. Если вы вызываете такие объявления из другого модуля, вам не нужно использовать этот плагин для компиляции этого модуля.

Последнее изменение: 12 июля 2023 г.
Сравнение с Java Вызов Kotlin из Java

© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/java-interop.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API