从Java访问Kotlin扩展函数

时间:2015-02-03 08:37:56

标签: java kotlin extension-function

是否可以从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文件,并搜索了很多内容,但我无法找到它。

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

10 个答案:

答案 0 :(得分:152)

在文件中声明的所有Kotlin函数将默认编译为同一包中的类中的静态方法,并且名称来自Kotlin源文件(首字母大写且" .kt&# 34; 扩展名替换为" 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);

答案 1 :(得分:20)

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中,可以通过其他方式声明扩展方法。 (例如,作为成员函数或作为伴随对象的扩展。)我将在稍后尝试扩展此答案。

答案 2 :(得分:3)

我有一个名为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中,在所需的导入import ....extensions.NumberFormattingKt;

之后,我可以通过以下方式通过文件 NumberFormattingKt 简单地访问它
String literString = NumberFormattingKt.formattedFuelAmountString(item.getAmount());

答案 3 :(得分:1)

您始终可以通过以下方法来查看从您的Kotlin代码生成的实际 Java代码:转到Tools > Kotlin > Show Kotlin Bytecode,然后单击Decompile。这可以极大地帮助您。如果您有MyModelExtensions.kt

,您的Java代码将如下所示:
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);

答案 4 :(得分:0)

此处的其他答案涵盖了调用位于Kotlin包文件顶层的扩展功能的情况。

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

解决方案非常简单

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

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

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

ExtensionFileName.foo(field1...)

基本上,它的作用是让接收者成为第一个论点,而其他论点保持在同一位置

所以例如。

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

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

在 Java 中,你称之为

ExtensionKt.showToast(context, message);

答案 7 :(得分:-1)

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

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

使用签名

创建一个新方法
public static int MyModelBar(MyModel obj) {
    return obj.name.length();
}

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

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

答案 8 :(得分:-1)

在扩展这样的类时:

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

还有更多examples here

答案 9 :(得分:-1)

它对我有用:

科特琳 kotlin

Java代码 enter image description here

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

,我可以使用以下命令从Java文件中调用它: StringUtilsKt.isNotNullOrEmpty(thestring)。

我的kotlin文件名为StringUtils