使用 kotlin-reflect 设置可为空的 UShort

Setting a nullable UShort using kotlin-reflect

为什么我不能在 kotlin 中使用反射设置 UShort?我将我的问题提取到单元测试中。

我的测试是这样的:

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")
    }
}

结果是

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)

我尝试过的事情:

内联 classes 有问题。如您所知,内联 classed 仍处于实验阶段,UShort 是一个内联 class,它充当 Short:

的包装器
public inline class UShort @PublishedApi internal constructor(@PublishedApi internal val data: Short) : Comparable<UShort>

让我们看一下您的代码的字节码。这是您的 DA 属性:

的摘要字节码
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

    ...

如您所知,内联 classes 应该在编译后被忽略和删除,但是因为您将 DA 定义为可空,编译后的类型仍然是 UShort 而不是 Short.

然而,当您在对象上调用 Int.toUShort 时,编译后的代码没有 UShort 的符号,而是转换为 Short(因为它是一个内联 class).这就是您收到 argument type mismatch 错误的原因。因为 setter 需要一个 UShort 但你给它一个 Short.
这解释了为什么您的代码使用 Short 而不是 UShort.

成功运行

无论如何,如果你真的需要在你的代码中使用 UShort,你不应该让它可以为空,而是使用 lateinit var 并且它工作正常。因为如果它不可为空,DA 属性 的类型在编译后会是 Short

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
   ...