首页 文章

当param不可为空时,未为reified类型插入空检查

提问于
浏览
1

TL;DR 具有reified类型的函数在生成代码时是否应考虑类型参数的可为空性?

Test case

考虑以下Kotlin代码;两种方法的唯一区别是类型绑定是否可为空( Any? )或不是( Any ) .

@Test
fun testNonNullableBound() {
    val x: Int = nonNullableBound()
}

@Test
fun testNullableBound() {
    val x: Int = nullableBound()
}

private inline fun <reified T : Any> nonNullableBound(): T {
    return unsafeMethod()
}

private inline fun <reified T : Any?> nullableBound(): T {
    return unsafeMethod()
}

其中 unsafeMethod 通过在Java中定义来颠覆类型系统:

public static <T> T unsafeMethod() { return null; }

这是Kotlin 1.1.4 .

Expected behaviour

我希望这些行为等效 - 类型是有效的,因此 T 的实际值已知是不可为空的,因此应该在 return 语句之前的函数内部应用null检查 .

Observed behaviour

这两种情况以不同的方式失败:

  • testNonNullableBound 按预期行为(由于对 unsafeMethod() 返回的值进行空检查而失败) .

  • testNullableBound 的行为与预期不符 - 在执行 x 赋值时,它与NPE失败 .

因此,似乎插入空检查基于类型绑定,而不是实际类型 .

Analysis

作为参考,相关字节码如下 . 请注意 testNonNullableBound 中添加的空检查 .

testNonNullableBound

public final testNonNullableBound()V
  @Lorg/junit/Test;()
   [...]
   L1
    LINENUMBER 28 L1
    INVOKESTATIC JavaStuff.unsafeMethod ()Ljava/lang/Object;
    DUP
    LDC "unsafeMethod()"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkExpressionValueIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
   [...]

testNullableBound

public final testNullableBound()V
  @Lorg/junit/Test;()
   [...]
   L1
    LINENUMBER 27 L1
    INVOKESTATIC JavaStuff.unsafeMethod ()Ljava/lang/Object;
   [...]

2 回答

  • 1

    因此,似乎插入空检查基于类型绑定,而不是实际类型 .

    完全正确,这是它应该如何工作!

    JVM函数不能具有不同的字节代码,具体取决于实际的泛型类型,因此相同的实现必须满足所有可能的结果 .

    内联不会影响这种情况,因为它遵循与普通函数相同的规则 . 它是以这种方式设计的,因此开发人员不那么惊讶 .

  • 1

    The problem isn't if the function is inlined or not.

    来自Kotlin的documentation

    Java中的任何引用都可能为null,这使得Kotlin对严格的null安全性的要求对来自Java的对象不切实际 . Java声明的类型在Kotlin中被特别处理并称为平台类型 . 对这些类型放宽空检查,因此对它们的安全保证与Java相同

    使用 @Nullable 注释,在java端,您可以强制kotlin检查类型是否可以为null(使用 @NotNull

    @Nullable
    public static <T> T unsafeMethod() { return null; }
    

相关问题