Why can't I set a UShort using reflection in kotlin? I extracted my problem into a unit test.
My test looks like this:
class Junk {
var DA: UShort? = null
}
class Tests {
@Test
fun testSetShort() {
var uut = Junk()
val value = 100
val expect = 100
val properties: Collection<KProperty<*>> = Junk::class.memberProperties
val property = properties.find { property -> property.name == "DA" }
if (property is KMutableProperty<*>) {
property.setter.call(uut, value.toUShort()) /* FAILS HERE */
}
assertEquals(expect, uut.DA)
System.err.println("ok")
}
}
The result is
argument type mismatch
java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97)
at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Instance.call(CallerImpl.kt:113)
at kotlin.reflect.jvm.internal.calls.InlineClassAwareCaller.call(InlineClassAwareCaller.kt:142)
at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108)
at Tests.testSetShort(testSetUshort.kt:24)
Things I have tried:
it's a problem with inline classes. as you know inline classed are still experimental and UShort
is an inline class which acts as a wrapper around Short
:
public inline class UShort @PublishedApi internal constructor(@PublishedApi internal val data: Short) : Comparable<UShort>
let's take a look at the bytecode for your code. this is the summarized bytecode of your DA
property:
private Lkotlin/UShort; DA
@Lorg/jetbrains/annotations/Nullable;() // invisible
// access flags 0x11
public final getDA-XRpZGF0()Lkotlin/UShort;
@Lorg/jetbrains/annotations/Nullable;() // invisible
...
public final setDA-ffyZV3s(Lkotlin/UShort;)V
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
@Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0
...
as you know inline classes should be ignored and deleted after compilation but because you defined DA
as nullable, the compiled type is still UShort
instead of Short
.
however, when you call Int.toUShort
on an object, the compiled code has no sign of UShort
and it converts to Short
instead(as it should because it's an inline class). that's why you get an argument type mismatch
error. because the setter needs a UShort
but you are giving it a Short
.
that explains why your code runs successfully with Short
instead of UShort
.
anyways, if you really need to use UShort
in your code, you should not make it nullable, use a lateinit var
instead and it works fine. because if it's not nullable, the DA
property's type would be Short
after compilation
var DA: UShort = 0u
//bytecode:
private S DA // S is JVM type for Short
// access flags 0x11
public final getDA-Mh2AYeg()S
...
// access flags 0x11
public final setDA-xj2QHRw(S)V
...