215

是否可以从 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 文件,也搜索了很多,但我找不到它。

我究竟做错了什么?这甚至可能吗?

4

10 回答 10

301

默认情况下,文件中声明的所有 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);
于 2015-02-06T11:39:59.240 回答
39

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

Extensions.kt给定包中的Kotlin 文件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 中,扩展方法可以用其他方式声明。(例如,作为成员函数或作为伴随对象的扩展。)

于 2016-04-06T13:21:35.617 回答
14

我有一个名为 NumberFormatting.kt 的 Kotlin 文件,它具有以下功能

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());
于 2018-10-01T13:50:22.890 回答
13

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

ExtensionFileName.foo(field1...)

基本上,它所做的是,它使接收者作为第一个论点,而其他论点仍然在同一个地方

所以例如。

您有扩展名(在文件 Extension.kt 中)

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

在 Java 中,您将其称为

ExtensionKt.showToast(context, message);
于 2021-02-27T18:32:08.570 回答
7

通过转到,然后单击 ,您始终可以看到从 Kotlin 代码生成的实际Java 代码。这可以极大地帮助你。在您的情况下,如果您有,Java 代码将如下所示Tools > Kotlin > Show Kotlin BytecodeDecompileMyModelExtensions.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);
于 2018-10-10T14:13:25.513 回答
1

这个对我有用:

科特林 科特林

Java 代码 在此处输入图像描述

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

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

我的 kotlin 文件名是 StringUtils

于 2020-04-09T22:50:07.327 回答
0

您需要在类文件中复制您的函数:

为前 Utils.kt 创建 Kotlin 文件

输入代码

  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
}

在 kotlin 中使用:

val str = ""
val lenth = str.getLength()

在 Java 中使用这个:

String str = "";
 Integer lenth = Utils.getLength(str);
于 2018-01-04T19:57:43.003 回答
-1

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

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

解决方案非常简单

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

于 2017-12-21T17:05:37.357 回答
-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);
  }
}

这里有更多的例子

于 2018-08-21T15:00:55.783 回答
-3

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

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

使用签名创建一个新方法

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

然后,Kotlin 将该函数映射到 form 的调用myModel.bar(),如果bar()在类中找不到,MyModel它会查找与它输出的签名和命名方案匹配的静态方法。请注意,这只是他们关于静态导入扩展而不覆盖已定义方法的陈述的假设。我在他们的消息来源中还没有走得足够远,无法确定。

因此,假设上述情况属实,则无法从普通的旧 java 代码调用 Kotlin 扩展,因为编译器只会看到在对象上调用未知方法并出错。

于 2015-02-03T09:14:01.730 回答