更改kotlin扩展函数接收器JVM名称

时间:2017-12-12 15:14:57

标签: java kotlin kotlin-interop java-interop

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

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

java中的函数看起来像这样

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

在我看来,$receiver使Java-interop感觉生成和不吸引人。

我知道您可以使用@JvmName注释和@file:JvmName之类的组合来更改java中的名称。

当我尝试将@JvmName用于receiver网站目标时,它说

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

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

2 个答案:

答案 0 :(得分:6)

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

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

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

fun Int.toPx() = toPx(this)

但是,预计不会编译,因为这两个函数将具有相同的JVM签名。因此,要消除歧义,请将@JvmName("...")添加到扩展程序,并(可选)将扩展名标记为inlinehide it from Java

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

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

使内联扩展的替代方法是annotating it with @JvmSynthetic。但是,内联也将取消呼叫开销。

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

答案 1 :(得分:0)

参数名称仅在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类中(另请参见here)。为此,您需要注释@file:JvmMultifileClass

因此,您还可以拥有一个Int扩展名,该扩展名与另一个文件中的像素完全无关,但仍将以同一Java类Ints结尾。