当参数不可为空时,空值检查不会插入到实体类型中
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
语句之前,应该在函数内部应用空检查。
观察到的行为
这两起案件以不同的方式失败:
-
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中的任何引用都可能为空,这使得Kotlin对来自Java的对象的严格无效安全的要求是不切实际的。 Java声明的类型在Kotlin中被专门处理,并被称为平台类型。 这种类型的空检查是放松的,所以它们的安全保证和Java一样
在java端使用@Nullable
注释,可以强制kotlin检查类型是否为null(使用@NotNull
)
@Nullable public static <T> T unsafeMethod() { return null; }
- 获得翻新异常方法返回类型不得在kotlin中包含类型变量或通配符
- 是否有相当于AssertJ库的Kotlin?
- 在浏览器中运行Kotlin HTML Builder
- 错误:错误:在类型为View的可为空的接收方上只允许使用safe(?。)或非null断言(!!)调用。
- 如何在jna中实现一个联合类型的结构sizeof()
- 什么是KTX(Kotlin扩展库),为什么它在Android开发中越来越受欢迎
- NoSuchMethodError:beforeCheckcastToFunctionOfArity没有静态方法
- Kotlin基础知识:如何添加或设置一个Map的元素?
- 为什么函数的结果不能作为Kotlin的when语句中的一个子句?