Доступ к функциям расширения Kotlin из Java

157

Можно ли получить доступ к функциям расширения из кода 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();

Однако это не работает. Среда не распознает bar()метод, и компиляция не удалась.

Что работает, использует статическую функцию из kotlin:

public fun bar(): Int {
   return 2*2
}

с помощью import com.test.extensions.ExtensionsPackageтак мой IDE , кажется, настроен правильно.

Я искал весь файл взаимодействия с Java из документации kotlin, а также много гуглил, но не смог его найти.

Что я делаю не так? Это вообще возможно?

Lovis
источник
Пожалуйста, уточните , не работает ? Компилируется, выдает исключение или как? Тоже есть import package com.test.extensions.MyModel?
meskobalazs
@meskobalazs см. мой отредактированный ответ.
Ловис
@meskobalazs также, я даже не могу импортировать это. Я могу только импортироватьcom.test.extensions.ExtensionsPackage
Lovis

Ответы:

232

Все функции 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);
goodwinnk
источник
1
Я понял, это означает, что я не могу использовать расширение от Kotlin до Java, если я не пишу статические функции?
AbdulMomen عبدالمؤمن
11
JFYI, Вы также можете изменить имя класса с помощью @file: JvmName ("<TheNameThatYouWant> Utils").
crgarridos
3
Что делать, если я создаю расширение с параметрами?
AmirG
3
Параметры @AmirG будут следовать примеру. В этом случае это будетbar(model, param1, param2);
Тим
Это была действительно быстрая помощь, большое спасибо
Вивек Гупта
31

Функция расширения верхнего уровня 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 методы расширения могут быть объявлены другими способами. (Например, как функция-член или как расширение объекта-компаньона.)

Александр Дубинский
источник
8

У меня есть файл 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());
Илкер Балтачи
источник
6

Вы всегда можете увидеть фактический код 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);
Адам Арольд
источник
0

Вам нужно продублировать ваши функции в файлах классов:

Создать файл 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);
NickUnuchek
источник
0

Меня устраивает:

Котлин Котлин

Java-код введите описание изображения здесь

Мой проект - старый проект Android, созданный на Java; Теперь я создал первый файл kotlin и добавил забавные расширения String. String.isNotNullOrEmpty (): Boolean {...}

и я мог бы вызвать его из файла Java, используя: StringUtilsKt.isNotNullOrEmpty (thestring).

Мой котлин файл называется StringUtils

Кристиан Диес
источник
-1

Другие ответы здесь покрывают случай вызова функции расширения, расположенной на верхнем уровне файла пакета Kotlin.

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

Решение невероятно простое .

Все, что вам нужно сделать, это аннотировать вашу функцию расширения как @JvmStatic, и вуаля! Ваш Java-код сможет получить к нему доступ и использовать его.

forresthopkinsa
источник
Почему отрицательный голос? Мой ответ правильный и оригинальный.
Форрестхопкинса
-2

Когда вы расширяете класс, как это:

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);
  }
}

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

Томас Бьерре
источник
-3

Насколько я могу сказать, это невозможно. Из моего прочтения документации по расширениям видно, что

public fun MyModel.bar(): Int {
    return this.name.length()
}

создает новый метод с подписью

public static int MyModelBar(MyModel obj) {
    return obj.name.length();
}

Затем Kotlin отображает эту функцию на вызовы формы myModel.bar(), где, если bar()не найден в MyModelклассе, он ищет статические методы, соответствующие схеме подписи и именования, которую он выводит. Обратите внимание, что это всего лишь предположение из их утверждений о том, что расширения статически импортируются и не переопределяют определенные методы. Я не зашел достаточно далеко в их источнике, чтобы знать наверняка.

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

johnw188
источник
3
Этот ответ не правильный, к ним можно получить доступ. Смотрите принятый ответ.
Джейсон Минард
И компилятор не ищет статические методы (особенно не статические методы Java). Он ищет методы расширения, которые были объявлены или импортированы в том же файле.
Кирилл Рахман