从 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("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;
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
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 标准库扩展函数 )