Как из Kotlin вызывать методы Java и наоборот?

Kotlin и Java полностью совместимы на уровне байткода JVM. Это означает, что Kotlin-код может напрямую вызывать Java-код, а Java-код — Kotlin. Такая интероперабельность — одна из причин популярности Kotlin на Android. Однако при вызовах между языками есть нюансы, связанные с синтаксисом, null-безопасностью, аннотациями и некоторыми языковыми особенностями.

Вызов Java-кода из Kotlin

Kotlin может напрямую вызывать Java-классы, методы, поля, создавать экземпляры классов и использовать стандартные библиотеки Java без необходимости адаптации.

Примеры

Java-класс:

public class JavaUtils {
public static int sum(int a, int b) {
return a + b;
}
public String greet(String name) {
return "Hello, " + name;
}
}

Kotlin-код:

val result = JavaUtils.sum(2, 3) // вызов static-метода
val greeting = JavaUtils().greet("Alex") // вызов instance-метода

Особенности вызова Java из Kotlin

1. Nullability

Kotlin требует указания null-безопасности. Все Java-типы по умолчанию считаются «платформенными» (String!), и Kotlin позволяет обращаться к ним как nullable или non-nullable — на усмотрение разработчика.

val name: String = JavaUtils().greet("Bob") // Kotlin считает, что greet() возвращает String!

Для большей строгости можно использовать аннотации в Java: @Nullable, @NotNull.

2. Геттеры и сеттеры

Java-геттеры/сеттеры работают как свойства в Kotlin:

public class Person {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
val person = Person()
person.name = "John" // setter
val n = person.name // getter

3. Методы со static

Java-методы со static можно вызывать напрямую:

val total = Math.max(10, 20) // вызов статического метода Java

Вызов Kotlin-кода из Java

Когда Kotlin-код вызывается из Java, нужно учитывать некоторые особенности Kotlin-языка, которые не существуют в Java.

Пример Kotlin-класса:

class KotlinHelper {
fun greet(name: String): String = "Hi, $name"
companion object {
fun version() = "1.0"
}
}

Java-вызов:

KotlinHelper helper = new KotlinHelper();
String msg = helper.greet("Sam");
// Обращение к companion object
String v = KotlinHelper.Companion.version();

Упрощение доступа через @JvmStatic:

companion object {
@JvmStatic
fun version() = "1.0"
}

Теперь Java-вызов:

String v = KotlinHelper.version(); // без .Companion

Частые Kotlin-фичи, которые требуют аннотаций или адаптаций для Java

1. Top-level функции

Kotlin-функции вне классов создают скрытый класс с суффиксом Kt.

// File: Utils.kt
fun sayHi() = "Hi"

Java:

String msg = UtilsKt.sayHi();

Чтобы задать имя явно:

@file:JvmName("Utils")

Теперь Java:

String msg = Utils.sayHi();

2. Default arguments

Java не поддерживает параметры по умолчанию. Kotlin решает это через перегрузку с @JvmOverloads.

class Printer {
@JvmOverloads
fun print(text: String, count: Int = 1) {
repeat(count) { println(text) }
}
}

Java:

new Printer().print("Hello"); // 1 раз
new Printer().print("Hello", 3); // 3 раза

3. Extension-функции

Из Java расширения нельзя вызвать напрямую, так как они превращаются в статические методы с передачей объекта как первого параметра.

fun String.hello() = "Hello, $this"

Java:

String msg = MyExtensionsKt.hello("Tom"); // имя файла + имя функции

4. Properties

Kotlin-свойства компилируются в get/set методы. В Java их вызывают как обычные методы:

val name: String = "Alex"

Java:

String n = kotlinClass.getName(); // автоматически сгенерированный геттер

5. Sealed классы

Java может использовать sealed-классы, но у них могут быть ограничения в доступе к наследникам — Java не может объявить собственные подклассы sealed-класса.

Kotlin-аннотации для Java-интероперабельности

  • @JvmStatic — генерирует static-методы.

  • @JvmOverloads — создает перегрузки с параметрами по умолчанию.

  • @JvmField — делает поле доступным напрямую без get/set.

  • @JvmName — задает имя класса или функции для JVM.

  • @JvmMultifileClass — объединяет несколько файлов в один .class.

  • @Throws — указывает, какие исключения выбрасываются функцией.

@Throws(IOException::class)
fun readFile(path: String): String { /\*...\*/ }

Kotlin и Java классы в Android Studio

Android Studio и Gradle поддерживают совместную компиляцию Kotlin и Java: вы можете свободно размещать .kt и .java файлы в одном проекте. Kotlin может использовать Java классы без каких-либо дополнительных действий, а Java может использовать Kotlin при соблюдении описанных выше правил.

Пример смешанного проекта

// KotlinHelper.kt
class KotlinHelper {
fun double(x: Int) = x \* 2
}
// MainActivity.java
KotlinHelper helper = new KotlinHelper();
int value = helper.double(10); // вызов Kotlin-кода из Java
// JavaUtil.java
public class JavaUtil {
public static String reverse(String s) {
return new StringBuilder(s).reverse().toString();
}
}
val reversed = JavaUtil.reverse("hello") // вызов Java-кода из Kotlin