是否可以从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似乎配置正确。

我从kotlin文档中搜索了整个Java-interop文件,也搜索了很多,但我没有找到它。

我做错了什么?这可能吗?


据我所知这是不可能的。从我对扩展文档的阅读来看,似乎

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

创建带有签名的新方法

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

然后,Kotlin将该函数映射到MyModel .bar()形式的调用,如果在MyModel类中没有找到bar(),它将查找与输出的签名和命名方案匹配的静态方法。请注意,这只是从他们关于静态导入扩展而不覆盖已定义方法的语句中得出的假设。我还没有深入了解他们的资料,无法确定。

因此,假设上述情况是正确的,那么就没有办法从普通的旧java代码中调用Kotlin扩展,因为编译器只会看到对对象调用未知的方法并输出错误。


在文件中声明的所有Kotlin函数在默认情况下将被编译为相同包中的类中的静态方法,并具有来自Kotlin源文件的名称(首字母大写和“。将“kt”扩展名替换为“kt”后缀)。为扩展函数生成的方法将有一个附加的扩展函数接收器类型的第一个参数。

将其应用于原始问题,Java编译器将看到名称为example.kt的Kotlin源文件

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

Kotlin顶级扩展函数被编译为Java静态方法。

给定Kotlin文件扩展名。foo中的Kt。栏包含:

fun String.bar(): Int {
    ...
}

等价的Java代码是:

package foo.bar;

class ExtensionsKt {
    public static int bar(String receiver) { 
        ...
    }
}

除非,也就是说,扩展。Kt包含这条线

@file:JvmName("DemoUtils")

在这种情况下,Java静态类将被命名为DemoUtils

在Kotlin中,扩展方法可以用其他方式声明。(例如,作为成员函数或作为伴生对象的扩展。)


这里的其他答案涵盖了调用位于Kotlin包文件顶层的扩展函数的情况。

然而,我的情况是,我需要调用位于类中的扩展函数。具体来说,我是在处理一个对象。

解决方法非常简单。

您所要做的就是将扩展函数注释为@JvmStatic,瞧!您的Java代码将能够访问并使用它。


当你像这样扩展一个类时:

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

这里还有更多的例子。


我有一个名为NumberFormatting的Kotlin文件。Kt有如下函数

fun Double.formattedFuelAmountString(): String? {
    val format = NumberFormat.getNumberInstance()
    format.minimumFractionDigits = 2
    format.maximumFractionDigits = 2
    val string = format.format(this)
    return string
}

在java中,我简单地通过以下方式访问文件NumberFormattingKt后,所需的导入导入....extensions.NumberFormattingKt;

String literString = NumberFormattingKt.formattedFuelAmountString(item.getAmount());

您总是可以看到从您的Kotlin代码生成的实际Java代码,通过转到Tools > Kotlin > Show Kotlin Bytecode,然后单击反编译。这可以极大地帮助你。在您的情况下,如果使用MyModelExtensions.kt, Java代码将如下所示

public final class MyModelExtensionsKt {
   public static final int bar(@NotNull MyModel $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      return $receiver.getName().length();
   }
}

你可以通过在包含bar的文件上使用@JvmName来改进:

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

这对我来说很管用:

科特林

Java代码

我的项目是一个用Java创建的旧android项目;现在我创建了第一个kotlin文件并添加了字符串扩展名 fun String.isNotNullOrEmpty():布尔{…}

我可以从java文件调用它使用: StringUtilsKt.isNotNullOrEmpty (thestring)。

我的kotlin文件名为StringUtils


使用更新的KotlinEx,您可以直接在java中调用扩展

ExtensionFileName.foo(field1...)

基本上,它所做的是,让接收者作为第一个参数,其他参数保持在相同的位置

所以For。

你有extension(在extension .kt文件中)

Context.showToast(message:String){ 
...
}

在Java中,称之为as

ExtensionKt.showToast(context, message);