groovy:000> ['homepages/gg','a','b','c','d'].inject([]) { list, conf -> if (!conf.contains('homepage')) { list << conf.trim() } }
ERROR java.lang.NullPointerException:
Cannot invoke method leftShift() on null object
at groovysh_evaluate$_run_closure1.doCall (groovysh_evaluate:3)
groovy:000> ['homepages/gg','a','b','c','d'].inject([]) { list, conf -> conf.contains('homepage') ? list : list << conf.trim() }
===> [a, b, c, d]
为什么我在第一种情况下获得NullPointerException
但在第二种情况下却没有?我使用的是Groovy 2.3.7。
答案 0 :(得分:4)
因为这段代码:
if (!conf.contains('homepage')) { list << conf.trim() }
如果不满足条件,不会返回任何内容。此else
没有if
。如果您添加else
返回有意义的值,则可以避免异常。
另一方面,三元运算符确实返回一个值,无论条件是否满足,所以这段代码:
conf.contains('homepage') ? list : list << conf.trim()
即使conf
包含'homepage'
,也会返回实际对象
要了解这是一个问题,请看看inject
方法的作用。
引用Javadoc:
遍历给定的Collection,将初始值与第一个项一起传递给2-arg闭包。结果与第二项一起传回(注入)到闭包中。新结果与第三个项目一起注入闭包,依此类推,直到整个集合被使用。
现在,我们来看一个稍微修改过的例子。我添加了一个println
来查看闭包参数变成了什么:
['homepages/gg','a','b','c','d'].inject([]) { list, conf -> println "[$list, $conf]"; if (!conf.contains('homepage')) { list << conf.trim() } }
运行此产生:
[[], homepages/gg]
[null, a]
Exception thrown
java.lang.NullPointerException: Cannot invoke method leftShift() on null object
at ConsoleScript9$_run_closure2.doCall(ConsoleScript9:2)
at ConsoleScript9.run(ConsoleScript9:2)
第一次调用将您传递的空数组作为单个Object
参数和列表的第一个元素,即"homepages/gg"
。这将传递给if
表达式。因为第一个元素"homepages/gg"
确实包含字符串"homepage"
,所以if
条件被评估为false。由于没有else
,不会返回任何内容。
由null
引用表示的 nothing ,由闭包的第一次评估返回,用于第二次评估,以及列表的下一个元素。
conf
现在等于"a
“,list
等于null
。此外,您使用<<
左移操作符{ {1}}列表(null
)。
因此例外。
这是一个等效的版本:
list << conf.trim()
输出符合预期:
['homepages/gg','a','b','c','d'].inject([]) { list, conf -> if (!conf.contains('homepage')) { list << conf.trim() } else list }