从 Java 访问 Kotlin 扩展函数

Posted

技术标签:

【中文标题】从 Java 访问 Kotlin 扩展函数【英文标题】:Accessing Kotlin extension functions from Java 【发布时间】:2015-04-02 09:20:04 【问题描述】:

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

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

【问题讨论】:

请详细说明不起作用?它会编译,抛出异常还是什么?你也有import package com.test.extensions.MyModel @meskobalazs 查看我编辑的答案。 @meskobalazs,我什至无法导入它。我只能导入com.test.extensions.ExtensionsPackage 【参考方案1】:

默认情况下,文件中声明的所有 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 的扩展,除非我写静态函数? JFYI,您还可以使用 @file:JvmName("Utils") 更改类的名称。 如果我创建一个带参数的扩展呢? @AmirG 参数将跟随实例。在这种情况下,它将是 bar(model, param1, param2); 这真是一个快速的帮助,非常感谢【参考方案2】:

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

【讨论】:

【参考方案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());

【讨论】:

【参考方案4】:

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

ExtensionFileName.foo(field1...)

基本上,它的作用是让接收者作为第一个参数,而其他参数保持在同一个地方

例如。

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

Context.showToast(message:String) 
...

在Java中,你称它为

ExtensionKt.showToast(context, message);

【讨论】:

【参考方案5】:

转到Tools > Kotlin > Show Kotlin Bytecode,然后单击Decompile,您始终可以看到从 Kotlin 代码生成的实际 Java 代码。这可以极大地帮助你。在您的情况下,如果您有 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);

【讨论】:

【参考方案6】:

它对我有用:

科特林

Java 代码

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

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

我的 kotlin 文件名是 StringUtils

【讨论】:

【参考方案7】:

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

创建 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

在 kotlin 中使用:

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

在 Java 中使用这个:

String str = "";
 Integer lenth = Utils.getLength(str);

【讨论】:

【参考方案8】:

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

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

解决方案非常简单

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

【讨论】:

为什么投反对票?我的回答是正确且原创的。【参考方案9】:

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

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。

【讨论】:

【参考方案10】:

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

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 扩展,因为编译器只会看到在对象上调用了未知方法并出错。

【讨论】:

这个答案不正确,可以访问。查看已接受的答案。 而且编译器并不在寻找静态方法(尤其不是 Java 静态方法)。它正在寻找已在同一文件中声明或导入的扩展方法。

以上是关于从 Java 访问 Kotlin 扩展函数的主要内容,如果未能解决你的问题,请参考以下文章

From Java To Kotlin:空安全扩展函数Lambda很详细,这次终于懂了

Kotlin 扩展函数和运算符重载[第一行代码 Kotlin 学习笔记]

Kotlin扩展函数 ③ ( 定义扩展文件 | 重命名扩展函数 | Kotlin 标准库扩展函数 )

Kotlin学习——扩展(扩展函数和属性以及伴生对象)

Kotlin系列之letwithrunapplyalso函数的使用

带有livedata的kotlin扩展函数返回null?