XTend空安全抛出NullPointerException异常

3

我正在把我的模板代码移植到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,测试会以“Not passed”的消息失败。但是只需将isNotNull方法的返回类型简单更改为Boolean(包装类):

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

出现了NullPointerException错误。检查生成的Java代码,我发现XTend使用了一个中间的布尔对象表达式,这就是NPE的原因。我是否没有理解XTend的空安全运算符(?.)的重点,或者我不能在运算符之后使用这样的方法?

谢谢。

2个回答

2
操作员的行为正确。异常是由于在if表达式中使用布尔值而抛出的,这需要自动拆箱。
如果尝试以下操作:
@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
    }
}

希望有所帮助。

2

简短回答:将第二个 null-safe 调用更改为常规调用。

即将

obj?.property?.isNotNull

转换为:

obj?.property.isNotNull

长答案:

文档这样描述了null-safe运算符:

在许多情况下,如果接收器为null,则表达式返回null是可以的。

这意味着你例子中的第二个调用property?.如果调用左侧为null,则不会调用isNotNull。相反,它将返回null。因此,条件“有效地”评估为:

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

(顺便说一下,在这种情况下,true 是多余的,但我还是保留它,以防你有其他条件要检查——我假设你只是将其简化为了true作为例子。)
如果您按照我的建议进行更改,obj?.property 将被评估,然后结果将传递给 isNotNull,生成以下结果:
if (true && isNotNull(null)) {

该方法将返回合适的Boolean对象,这将按预期自动取消装箱。

注意事项

在您的第一种isNotNull形式中,即返回原始boolean的形式,您实际上应该获得警告,例如“对具有基本值的特征isNotNull进行空安全调用,将使用默认值false。”

这是因为您扩展了空安全调用的意图,其目的是在左侧操作数为空(null)时返回null而不调用右侧方法。但是如果您的isNotNull返回原始的boolean,整个表达式显然无法计算为null,因此Xtend将使用默认值,布尔值的默认值为false

为了以不同的方式强调问题 - 它在不调用isNotNull的情况下评估为false - 这意味着即使在运算符后使用了方法isNull,它仍将返回false

文档也提到了这种行为(尽管是一般性的):

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

因此,我建议始终在空安全调用的右侧使用非原始返回值。但是,如果您将isNotNull转换为我建议的常规调用,那么此规则不适用,任何返回类型都可以。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接