Доступ к функциям расширения Kotlin из Java
Можно ли получить доступ к функциям расширения из кода Java?
Я определил функцию расширения в файле Kotlin.
package com.test.extensions
import com.test.model.MyModel
/**
*
*/
public fun MyModel.bar(): Int {
return this.name.length()
}
Где MyModel
- (сгенерированный) класс java.
Теперь я хотел получить к нему доступ в своем нормальном java-коде:
MyModel model = new MyModel();
model.bar();
Однако это не работает. IDE не распознает метод bar()
, и компиляция завершится с ошибкой.
Что работает с статической функцией от kotlin:
public fun bar(): Int {
return 2*2
}
используя import com.test.extensions.ExtensionsPackage
, поэтому моя IDE настроена правильно.
Я искал весь файл Java-interop из документов kotlin, а также много искал, но не смог найти его.
Что я делаю неправильно? Возможно ли это?
Ответы
Ответ 1
Все функции Kotlin, объявленные в файле, будут по умолчанию ставить статические методы в классе внутри одного и того же пакета и с именем, полученным из исходного файла Kotlin (первая буква с заглавной буквы и расширение ".kt" заменено на "Kt" суффикс). Методы, сгенерированные для функций расширения, будут иметь дополнительный первый параметр с типом приемника функции расширения.
Применяя его к исходному вопросу, компилятор Java увидит исходный файл Kotlin с именем example.kt
package com.test.extensions
public fun MyModel.bar(): Int { /* actual code */ }
как если бы был объявлен следующий класс Java
package com.test.extensions
class ExampleKt {
public static int bar(MyModel receiver) { /* actual code */ }
}
Поскольку ничего не происходит с расширенным классом с точки зрения Java, вы не можете просто использовать точечный синтаксис для доступа к таким методам. Но они по-прежнему можно назвать обычными статическими методами Java:
import com.test.extensions.ExampleKt;
MyModel model = new MyModel();
ExampleKt.bar(model);
Статический импорт может использоваться для класса ExampleKt:
import static com.test.extensions.ExampleKt.*;
MyModel model = new MyModel();
bar(model);
Ответ 2
Функция расширения верхнего уровня Kotlin скомпилирована как статические методы Java.
Данный файл Kotlin Extensions.kt
в пакете foo.bar
, содержащий:
fun String.bar(): Int {
...
}
Эквивалентный код Java будет следующим:
package foo.bar;
class ExtensionsKt {
public static int bar(String receiver) {
...
}
}
Если только Extensions.kt
не содержало строку
@file:JvmName("DemoUtils")
В этом случае статический класс Java будет назван DemoUtils
В Kotlin методы расширения могут быть объявлены другими способами. (Например, как функция-член или как расширение объекта-компаньона.)
Ответ 3
У меня есть файл Kotlin с именем NumberFormatting.kt, который имеет следующую функцию
fun Double.formattedFuelAmountString(): String? {
val format = NumberFormat.getNumberInstance()
format.minimumFractionDigits = 2
format.maximumFractionDigits = 2
val string = format.format(this)
return string
}
В Java я просто получить доступ к нему через файл NumberFormattingKt следующим образом после необходимого импорта import....extensions.NumberFormattingKt;
String literString = NumberFormattingKt.formattedFuelAmountString(item.getAmount());
Ответ 4
Вы всегда можете увидеть реальный код Java, который генерируется из вашего кода Kotlin, перейдя в Tools > Kotlin > Show Kotlin Bytecode
, затем нажав Decompile
. Это может вам очень помочь. В вашем случае код Java будет выглядеть так, если у вас есть MyModelExtensions.kt
public final class MyModelExtensionsKt {
public static final int bar(@NotNull MyModel $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.getName().length();
}
}
Вы можете улучшить это, используя @JvmName
для файла, содержащего bar
:
@file:JvmName("MyModels")
package io.sspinc.datahub.transformation
public fun MyModel.bar(): Int {
return this.name.length
}
и это приведет к следующему коду:
public final class MyModels {
public static final int bar(@NotNull MyModel $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.getName().length();
}
}
Использование MyModels
согласуется с тем, что Effective Java предлагает для служебных классов. Вы также можете переименовать ваш метод следующим образом:
public fun MyModel.extractBar(): Int {
return this.name.length
}
тогда со стороны Java это будет выглядеть идиоматично:
MyModels.extractBar(model);
Ответ 5
Вам нужно продублировать ваши функции в файлах классов:
Создать файл Kotlin, например, для Utils.kt
введите код
class Utils {
companion object {
@JvmStatic
fun String.getLength(): Int {//duplicate of func for java
return this.length
}
}
}
fun String.getLength(): Int {//kotlin extension function
return this.length
}
ИЛИ ЖЕ
class Utils {
companion object {
@JvmStatic
fun getLength(s: String): Int {//init func for java
return s.length
}
}
}
fun String.getLength(): Int {//kotlin extension function
return Utils.Companion.getLength(this)//calling java extension function in Companion
}
В котлине используют:
val str = ""
val lenth = str.getLength()
В Java используйте это:
String str = "";
Integer lenth = Utils.getLength(str);
Ответ 6
Я знаю, что это старый пост, но я только что нашел этот пост, пытаясь сделать то же самое сам. Прочитав ответы и попробовав это самостоятельно в смешанном проекте Kotlin/Java, я считаю, что ответ таков:
- Да, вы можете получить к ним доступ, используя методы, описанные выше, и
- Когда вы обращаетесь к ним таким образом, на самом деле это не функция расширения Kotlin, а просто статический метод Java.
Я хотел добавить функции расширения Kotlin, которые я мог бы использовать из своего кода Java, но в основном он просто стал функцией Kotlin, то есть фактический объект Java не расширялся.
Я пытался расширить Spring Boot WebClient, который я использовал во многих местах своего Java-кода, с помощью функций расширения Kotlin:
SomeControllerClass.java:
WebClient webClient = webClient.buildMyWay()
WebClientExt.kt:
fun WebClient.buildMyWay(): WebClient {
}
Здесь я пытался получить функцию "buildMyWay" бесплатно для любого экземпляра WebClient где-нибудь еще в моем коде, что, я считаю, является целью функций расширения Kotlin.
Это не сработало и в конечном итоге просто:
SomeControllerClass.java:
WebClient webClient = WebClientExt.buildMyWay()
WebClientExt.kt:
fun buildMyWay(): WebClient {
}
я считаю, что это на самом деле не функция расширения Kotlin, а просто функция Kotlin, которая является просто общедоступным статическим вспомогательным методом Java.
Ответ 7
Другие ответы здесь касаются случая вызова функции расширения, расположенной на верхнем уровне файла пакета Kotlin.
Однако, мой случай состоял в том, что мне нужно было вызвать функцию расширения, расположенную внутри класса. В частности, я имел дело с Object.
Решение невероятно просто.
Все, что вам нужно сделать, это аннотировать вашу функцию расширения как @JvmStatic
и voila! Ваш Java-код сможет получить к нему доступ и использовать его.
Ответ 8
Насколько я могу судить, это невозможно. Из моего чтения документов расширений кажется, что
public fun MyModel.bar(): Int {
return this.name.length()
}
создает новый метод с подписью
public static int MyModelBar(MyModel obj) {
return obj.name.length();
}
Затем Kotlin отображает эту функцию на вызовы формы myModel.bar()
, где if bar()
не найден в классе MyModel
, он ищет статические методы, соответствующие схеме сигнатуры и именования, которую он выводит. Обратите внимание, что это всего лишь предположение из их утверждений о том, что расширения, статически импортированные и не переопределяющие определенные методы. Я не получил достаточно далеко от их источника, чтобы точно знать.
Итак, если предположить, что это верно, нет никаких причин, чтобы расширения Kotlin вызывались из простого старого java-кода, поскольку компилятор просто увидит, что неизвестный метод вызывается на объект и ошибка.
Ответ 9
Когда вы расширяете класс, как это:
fun String.concatenatedLength(str: String): Int {
return (this.length + str.length)
}
fun f() {
var len = "one string".concatenatedLength("another string")
println(len)
}
Это скомпилируется в это:
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
public final class ExampleKt {
public static final int concatenatedLength(@NotNull String $receiver, @NotNull String str) {
Intrinsics.checkParameterIsNotNull((Object) $receiver, (String) "$receiver");
Intrinsics.checkParameterIsNotNull((Object) str, (String) "str");
return $receiver.length() + str.length();
}
public static final void f() {
int len = ExampleKt.concatenatedLength("one string", "another string");
System.out.println(len);
}
}
Здесь есть еще примеры.