Подтвердить что ты не робот

Доступ к функциям расширения 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, а также много искал, но не смог найти его.

Что я делаю неправильно? Возможно ли это?

4b9b3361

Ответ 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, я считаю, что ответ таков:

  1. Да, вы можете получить к ним доступ, используя методы, описанные выше, и
  2. Когда вы обращаетесь к ним таким образом, на самом деле это не функция расширения 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);
  }
}

Здесь есть еще примеры.