我将模板代码移植到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安全操作符(?。)的意思,或者我不能在运算符之后使用这样的方法吗?
感谢。
答案 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
转换为常规调用,则此规则不适用,并且返回类型都可以。