XTend null safe抛出NullPointerException

时间:2015-06-13 02:28:27

标签: nullpointerexception xtend

我将模板代码移植到XTend。在某些时候,我在测试用例中有这种类型的条件处理:

@Test
def xtendIfTest() {
    val obj = new FD
    if (true && obj?.property?.isNotNull) {
        return
    }
    fail("Not passed")
}

def boolean isNotNull(Object o) {
    return o != null
}
class FD {
 @Accessors
 String property
}

这可以正常工作,因为属性为null并且测试将失败并且"未通过"信息。但是将isNotNull方法的返回类型简单更改为Boolean(包装器):

def Boolean isNotNull(Object o) {
    return o != null
}

因NullPointerException而失败。检查生成的java代码,我可以看到XTend正在使用中间的布尔对象表达式,这是NPE的原因。我错过了XTend null安全操作符(?。)的意思,或者我不能在运算符之后使用这样的方法吗?

感谢。

2 个答案:

答案 0 :(得分:2)

操作员行为正常。抛出异常是因为在if-expression中使用了布尔值,这需要自动取消装箱。

如果您尝试以下操作:

@Test
def xtendIfTest() {
    val Boolean obj = null
    if (obj) {
        return
    }
    fail("Not passed")
}

您还将遇到NullPointerException。

这与Java语言规范(https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.8)一致 - 当需要自动取消装箱时,这会产生NullPointerException:

@Test
public void test() {
    Boolean value = null;
    if (value) { // warning: Null pointer access: This expression of type Boolean is null but requires auto-unboxing
        // dead code
    }
}

希望有所帮助。

答案 1 :(得分:0)

简答:将第二个空安全呼叫更改为常规呼叫。

即。变化

obj?.property?.isNotNull

到此:

obj?.property.isNotNull

答案很长:

docs因此描述了空安全运算符:

  

在很多情况下,如果a表达式返回null是可以的   接收器为空

这意味着,如果通话的左侧是property?.,则示例中的第二个电话isNotNull甚至不会拨打null。相反,它将返回null。所以有条件的“有效”评估为:

if (true && null) {  // causes NPE when java tries to unbox the Boolean 

(顺便说一下 - true在这种情况下是多余的,但我保留它以防你有另一个条件要检查 - 我假设你只是将它简化为{{1对于这个例子。)

如果您进行了我建议的更改,则会对true进行评估,然后将结果传递给obj?.property,并对此进行评估:

isNotNull

返回正确的if (true && isNotNull(null)) { 对象,该对象将按预期自动取消装箱。

谨慎之道

在你的Boolean的第一种形式,即一个返回原语isNotNull,你应该得到一个警告,例如“原始值特征的空安全调用isNotNull,将使用默认值false ”

这是因为你正在扩展空安全调用的意图,即如果运算符的左侧是boolean,则返回null而不调用右侧方法。但是,如果您的null返回原始isNotNull,则整个表达式显然无法评估为boolean,因此Xtend使用默认值,即布尔值null。 / p>

要以不同的方式强调问题 - 它评估为false 而不调用false - 这意味着即使您使用了isNotNull之后的方法运算符,它仍然返回isNull

文档也提到了这种行为(虽然一般来说):

  

对于基本类型,返回默认值(例如,0表示int)。   在某些情况下,这可能不是您想要的,因此会发出警告   默认提出

因此,我建议始终在空安全调用的右侧使用非原始返回值。但是,如果您要按照我的建议将false转换为常规调用,则此规则不适用,并且返回类型都可以。