0

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

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

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

这是 Kotlin 1.1.4。

预期行为

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

观察到的行为

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

  • testNonNullableBound行为符合预期(由于对返回的值进行空检查而失败unsafeMethod())。
  • testNullableBound未按预期运行 - 分配给x.

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

分析

供参考,相关字节码如下。注意添加的空检查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;
   [...]
4

2 回答 2

1

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

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

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

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

于 2017-09-05T18:31:35.430 回答
1

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

来自 Kotlin 的文档

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

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

@Nullable
public static <T> T unsafeMethod() { return null; }
于 2017-09-05T20:37:44.233 回答