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

时间:2017-09-05 18:08:24

标签: kotlin kotlin-null-safety

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

测试用例

考虑以下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。

预期行为

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

观察到的行为

这两起案件以不同方式失败:

  • testNonNullableBound的行为符合预期(由于对unsafeMethod()返回的值进行空检查而失败)。
  • testNullableBound没有按预期行事 - 在执行x分配时失败了NPE。

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

分析

作为参考,相关的字节码如下。请注意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 个答案:

答案 0 :(得分:1)

  

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

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

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

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

答案 1 :(得分:1)

如果该功能是内联的,则问题不是。

来自Kotlin的documentation

  

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

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

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