8

这是一个普遍的问题。假设我有一个用 kotlin 编写的扩展函数,它将 DP 转换为 PX 并返回一个 NonNull Int

fun Int.toPx() {  /** implementation */ }

java中的函数看起来像这样

public int toPx(int $receiver) {  /** implementation */ }

在我看来,这$receiver使得 Java 互操作感觉生成且不受欢迎。

我知道您可以将@JvmName注释与某些组合一起使用,例如@file:JvmName更改 java 中的名称。

当我尝试@JvmNamereceiver站点目标一起使用时,它说

“此注释不适用于目标type usage和使用站点目标@receiver

有没有办法克服这个问题并更改接收者的名称,如果没有,最好的选择是什么。

4

2 回答 2

10

@JvmName只能应用于文件的函数、属性访问器和顶级包外观。不支持参数名称。

基本上,您可以定义两个函数,一个带有简单参数,另一个带有接收器:

fun toPx(value: Int) { /* implementation */ }

fun Int.toPx() = toPx(this)

但是,不出所料,这不会编译,因为这两个函数将具有相同的 JVM 签名。因此,要消除它们的歧义,请添加@JvmName("...")到扩展名并(可选)将扩展名标记为inline

fun toPx(value: Int) { /* implementation */ }

@JvmName("toPxExtension") @Suppress("nothing_to_inline")
inline fun Int.toPx() = toPx(this)

要对 Java 隐藏扩展功能,您还可以使用@JvmSynthetic.

此解决方案的缺点是顶级函数toPx泄漏到查看包的文件的 IDE 完成范围中。

于 2017-12-12T15:19:36.137 回答
1

参数名称仅在 Java 中与文档相关(作为 IDE 中的方法签名提示,当您调用方法时)。与 Kotlin 不同,它从来不是调用代码的一部分。

如果您在现有类型上定义扩展方法,我发现一个好的方法是以描述接收者的方式命名文件。虽然这对于 Kotlin 无关紧要(因为扩展方法将在没有文件名的情况下被调用),但对于其他 JVM 语言却是这样。因此,接收器类型/含义不是由参数名称表示,而是由类名称表示。

你已经知道了@file:JvmName,所以利用它来发挥你的优势:

@file:JvmName("Ints")

fun Int.toPx() { ... }

在科特林:

val value = 328
val px = value.toPx()

在 Java 中,代码非常接近 Kotlin 对应部分:

int value = 328;
Pixel px = Ints.toPx(value);

当然可以是更长的名称,例如IntExtensions是否有助于提高可读性。

请注意,在 Kotlin 中,明确允许@file:JvmName在多个文件中重用相同的类名,并且扩展函数将组合在一个 JVM 类中(另请参见此处)。为此,您需要注释@file:JvmMultifileClass

因此,您还可以拥有一个Int与另一个文件中的像素完全无关的扩展名,但它仍然会出现在同一个 Java 类Ints中。

于 2018-08-09T08:33:22.757 回答