XTend 空安全抛出 NullPointerException
XTend null safe throws NullPointerException
我正在将我的模板代码移植到 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
}
这按预期工作,因为 属性 为空并且测试将失败并显示 "Not passed" 消息。但是将 return 类型的 isNotNull 方法简单更改为 Boolean (wrapper):
def Boolean isNotNull(Object o) {
return o != null
}
失败并出现 NullPointerException。为此检查生成的 java 代码,我可以看到 XTend 使用了一个中间布尔对象表达式,这就是 NPE 的原因。我是否遗漏了 XTend 空安全运算符 (?.) 的要点,或者我不能在运算符之后使用这样的方法?
谢谢。
接线员行为正常。抛出异常是因为在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
}
}
希望对您有所帮助。
简答:将第二个空安全调用更改为常规调用。
即变化
obj?.property?.isNotNull
对此:
obj?.property.isNotNull
长答案:
docs 是这样描述空安全运算符的:
In many situations it is ok for an expression to return null if a
receiver was null
这意味着您示例中的第二个调用,如果调用的左侧是 null
,property?.
甚至不会调用 isNotNull
。相反,它将 return null
。所以条件 "effectively" 的计算结果为:
if (true && null) { // causes NPE when java tries to unbox the Boolean
(顺便说一句 - true
在这种情况下是多余的,但我保留它以防万一你有另一个条件要检查 - 我假设你只是将它简化为 true
对于这个例子。)
如果您进行我建议的更改,将评估 obj?.property
,然后将结果传递给 isNotNull
,评估为:
if (true && isNotNull(null)) {
哪个 return 是正确的 Boolean
对象,将按预期自动拆箱。
注意事项
在 isNotNull
的第一种形式中,即 returning 原语 boolean
,你实际上应该得到像 "Null-safe call of primitive-valued feature isNotNull, default value false will be used".
这样的警告
这是因为您扩展了空安全调用的意图,即 return null
如果运算符的左侧是 [=],则不调用右侧方法16=]。但是如果你的 isNotNull
return 是一个原始的 boolean
,整个表达式显然不能计算为 null
,所以 Xtend 使用默认值,即 false
对于布尔值。
为了以不同的方式强调这个问题 - 它的计算结果为 false
而没有 调用 isNotNull
- 这意味着即使你使用了一个方法 isNull
在运算符之后,它会 still return false
!
文档还提到了这种行为(尽管是笼统的说法):
For primitive types the default value is returned (e.g. 0 for int).
This may not be what you want in some cases, so a warning will be
raised by default
因此,我建议始终在空安全调用的右侧使用非原始 return 值。但是,如果您要按照我的建议将 isNotNull
转换为常规调用,则此规则不适用,并且 return 类型都可以。
我正在将我的模板代码移植到 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
}
这按预期工作,因为 属性 为空并且测试将失败并显示 "Not passed" 消息。但是将 return 类型的 isNotNull 方法简单更改为 Boolean (wrapper):
def Boolean isNotNull(Object o) {
return o != null
}
失败并出现 NullPointerException。为此检查生成的 java 代码,我可以看到 XTend 使用了一个中间布尔对象表达式,这就是 NPE 的原因。我是否遗漏了 XTend 空安全运算符 (?.) 的要点,或者我不能在运算符之后使用这样的方法?
谢谢。
接线员行为正常。抛出异常是因为在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
}
}
希望对您有所帮助。
简答:将第二个空安全调用更改为常规调用。
即变化
obj?.property?.isNotNull
对此:
obj?.property.isNotNull
长答案:
docs 是这样描述空安全运算符的:
In many situations it is ok for an expression to return null if a receiver was null
这意味着您示例中的第二个调用,如果调用的左侧是 null
,property?.
甚至不会调用 isNotNull
。相反,它将 return null
。所以条件 "effectively" 的计算结果为:
if (true && null) { // causes NPE when java tries to unbox the Boolean
(顺便说一句 - true
在这种情况下是多余的,但我保留它以防万一你有另一个条件要检查 - 我假设你只是将它简化为 true
对于这个例子。)
如果您进行我建议的更改,将评估 obj?.property
,然后将结果传递给 isNotNull
,评估为:
if (true && isNotNull(null)) {
哪个 return 是正确的 Boolean
对象,将按预期自动拆箱。
注意事项
在 isNotNull
的第一种形式中,即 returning 原语 boolean
,你实际上应该得到像 "Null-safe call of primitive-valued feature isNotNull, default value false will be used".
这是因为您扩展了空安全调用的意图,即 return null
如果运算符的左侧是 [=],则不调用右侧方法16=]。但是如果你的 isNotNull
return 是一个原始的 boolean
,整个表达式显然不能计算为 null
,所以 Xtend 使用默认值,即 false
对于布尔值。
为了以不同的方式强调这个问题 - 它的计算结果为 false
而没有 调用 isNotNull
- 这意味着即使你使用了一个方法 isNull
在运算符之后,它会 still return false
!
文档还提到了这种行为(尽管是笼统的说法):
For primitive types the default value is returned (e.g. 0 for int). This may not be what you want in some cases, so a warning will be raised by default
因此,我建议始终在空安全调用的右侧使用非原始 return 值。但是,如果您要按照我的建议将 isNotNull
转换为常规调用,则此规则不适用,并且 return 类型都可以。