Changing kotlin extension function receiver JVM name

久未见 提交于 2021-01-27 04:30:34

问题


This is a general question. Let's say I have an extension function written in kotlin which converts DP to PX and return a NonNull Int

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

The function in java will look something like this

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

In my opinion the $receiver makes the Java-interop feels generated and uninviting.

I know that you can use the @JvmName annotation with some combinations like @file:JvmName to change the name in java.

When i'm trying to use @JvmName with the receiver site target it says

"This annotation is not applicable to target type usage and use site target @receiver"

Is there a way to overcome that and change the name of the receiver and if not what is the best alternative.


回答1:


@JvmName can only be applied to functions, property accessors and top-level package facades for files. Parameter names are not supported.

Basically, you can define two functions, one taking a simple parameter and another one with receiver:

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

fun Int.toPx() = toPx(this)

But, expectedly enough, this won't compile because the two functions would have the same JVM signatures. So, to disambiguate them, add @JvmName("...") to the extension and (optionally) mark the extension as inline:

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

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

To hide the extension function from Java, you can also annotate it with @JvmSynthetic.

The downside of this solution is the top-level function toPx leaking into the IDE completion scope of the files that see the package.




回答2:


The parameter name is only relevant in Java regarding documentation (as a method signature hint in the IDE, when you call a method). Unlike in Kotlin, it is never part of the calling code.

If you define extension methods on existing types, I've found that a good approach is to name the file in a way that describes the receiver. While this doesn't matter for Kotlin (as extension methods will be called without the file name), it does for other JVM languages. Thus, the receiver type/meaning is not expressed by the parameter name, but by the class name.

You're already aware of @file:JvmName, so use it to your advantage:

@file:JvmName("Ints")

fun Int.toPx() { ... }

In Kotlin:

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

In Java, the code is very close to the Kotlin counterpart:

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

Can of course be a longer name such as IntExtensionsif that helps readability.

Note that in Kotlin, it is explicitly allowed to reuse the same class name in @file:JvmName across multiple files, and the extensions functions will be combined in a single JVM class (see also here). You need the annotation @file:JvmMultifileClass for this.

Thus, you could also have an Int extension completely unrelated to pixels in another file, yet it would still end up in the same Java class Ints.



来源:https://stackoverflow.com/questions/47775870/changing-kotlin-extension-function-receiver-jvm-name

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!