【问题标题】:Accessing Kotlin extension functions from Java从 Java 访问 Kotlin 扩展函数
【发布时间】: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

标签: java kotlin extension-function


【解决方案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 静态方法)。它正在寻找已在同一文件中声明或导入的扩展方法。
                猜你喜欢
                • 2017-12-21
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多