繁体   English   中英

当参数不可为空时,未插入空值化类型的检查

[英]Null checks not inserted for reified type when param is non-nullable

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

测试用例

考虑以下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的实际值不可为空,因此应在return语句之前的函数内部应用null检查。

观察到的行为

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

  • 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;
   [...]

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

完全正确,这就是应该的工作方式!

根据实际的通用类型,JVM函数不能具有不同的字节码,因此相同的实现必须满足所有可能的结果。

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

问题是否在于函数是否内联。

从Kotlin的文档中

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM