2017-09-05 114 views
2

TL; DR在生成代码时,具有特定类型的函数是否应考虑类型参数的可空性?当参数不可为空时,为空化类型插入的空检查

测试用例

考虑以下科特林代码;这两种方法之间的唯一区别是类型绑定是否可以为空(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; } 

这是科特林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; 
    [...] 
+0

'空检查应该在函数内部要应用的回报statement.' 之前为什么你在做这样的假设? 您首先调用'unsafeMethod',然后检查它的可空性, – crgarridos

+0

@crgarridos - 因为这就是非泛型情景中发生的情况(我假设泛化类型与此类似)。 –

回答

1

所以它出现null检查的插入是基于约束,而不是实际的类型的类型。

没错,这是它应该如何工作!

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

内联不会影响这种情况,因为它遵循与正常函数相同的规则。它是这样设计的,所以开发人员不会感到惊讶。

+0

如果内联函数应该与非内联函数具有相同的语义,那么这条推理总是有意义的。 (虽然FWIW,这个特定的开发*被行为感到惊讶;) –

+0

为了完整性,你知道任何文档/规格/等。说明这一点? (即内联函数应该像正常函数那样在语义上表现) –

+0

@OliverCharlesworth,因为它没有另外声明,我断定内联泛型的规则与其他地方相同 – voddan

1

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

从科特林的documentation

在Java中的任何参考可以为null,这使得严格的空安全不切实际的,从Java未来的对象科特林的要求。 Java声明的类型在Kotlin中被专门处理并称为平台类型。空检查是放松对于这样的类型,所以对他们的安全保障是一样的Java

使用@Nullable注解,在Java端,你可以强制科特林检查的类型可以是零或不是(使用@NotNull

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

这句话很有道理。然而,我的误解/困惑是关于*运行时*检查编译器插入,而不是静态检查。 –