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 回答
完全正确,这是它应该如何工作!
JVM函数不能具有不同的字节代码,具体取决于实际的泛型类型,因此相同的实现必须满足所有可能的结果 .
内联不会影响这种情况,因为它遵循与普通函数相同的规则 . 它是以这种方式设计的,因此开发人员不那么惊讶 .
The problem isn't if the function is inlined or not.
来自Kotlin的documentation:
使用
@Nullable
注释,在java端,您可以强制kotlin检查类型是否可以为null(使用@NotNull
)