问题
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 IntExtensions
if 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