从Java访问Kotlin扩展功能


156

是否可以从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互操作文件,并且在谷歌上搜索了很多,但是找不到。

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


请详细说明行不通吗?它会编译,引发异常还是什么?也有吗import package com.test.extensions.MyModel
meskobalazs 2015年

@meskobalazs可以看到我编辑的答案。
罗维斯

@meskobalazs,我什至无法导入。我只能导入com.test.extensions.ExtensionsPackage
Lovis,

Answers:


230

默认情况下,文件中声明的所有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);

1
我知道,这意味着除非编写静态函数,否则我不能使用从Kotlin到Java的扩展?
AbdulMomenعبدالمؤمن17年

11
JFYI,您也可以使用@file:JvmName(“ <TheNameThatYouWant> Utils”)更改类的名称。
crgarridos

3
如果我用参数创建扩展名怎么办?
AmirG

3
@AmirG参数将跟随实例。在这种情况下,应为bar(model, param1, param2);
蒂姆(Tim)

这确实是个快速帮助,非常感谢
Vivek Gupta

31

Kotlin顶级扩展功能被编译为Java静态方法。

Extensions.kt软件包foo.bar中的给定Kotlin文件包含:

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

我有一个名为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());

6

转到,然后单击,您始终可以看到从Kotlin代码生成的实际 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与有效Java建议的实用程序类一致。您也可以像这样重命名您的方法:

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

然后从Java的角度来看,这是惯用的:

MyModels.extractBar(model);

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

0

这个对我有用:

科特林 科特林

Java代码 在此处输入图片说明

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

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

我的kotlin文件名为StringUtils


-1

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

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

解决方案非常简单

您所要做的就是将扩展功能注释为@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将该函数映射到form的调用myModel.bar(),如果bar()MyModel类中未找到该函数,它将查找与其输出的签名和命名方案匹配的静态方法。请注意,这只是他们关于静态导入扩展而不覆盖定义的方法的声明的假设。我对他们的消息还不够了解。

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


3
这个答案不正确,可以访问它们。查看已接受的答案。
杰森·米纳德

而且编译器没有在寻找静态方法(特别是不是Java静态方法)。它正在寻找已在同一文件中声明或导入的扩展方法。
Kirill Rakhman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.