为什么此Scala代码返回NullPointerException?

时间:2018-09-19 04:26:28

标签: scala

代码是:

def mergeTrees(t1: TreeNode, t2: TreeNode): TreeNode = {
        if (t1 == null && t2 == null) null
        else if (t1 == null) t2
        else if (t2 == null) t1

        val root = new TreeNode(t1.value + t2.value)
        root.left = mergeTrees(t1.left, t2.left)
        root.right = mergeTrees(t1.right, t2.right)

        root
}

如果我将其更改为:

def mergeTrees(t1: TreeNode, t2: TreeNode): TreeNode = {
        if (t1 == null && t2 == null) null
        else if (t1 == null) t2
        else if (t2 == null) t1
        else {
            val root = new TreeNode(t1.value + t2.value)
            root.left = mergeTrees(t1.left, t2.left)
            root.right = mergeTrees(t1.right, t2.right)

            root
        }
}

然后它起作用。这是什么原因造成的?

3 个答案:

答案 0 :(得分:1)

在第一个示例中,您有一个if语句,该语句计算一个值,然后将该值丢弃。然后假设t1t2都不是null,然后计算根。

第二个示例添加了else子句,该子句仅在t1t2都不是null时使用。现在,该函数的结果就是该函数中最后一条语句(即整个if语句)的结果。 if语句其他分支上的那些值很有用(它们可能是函数的最后一个值,因此也就是返回值)。

答案 1 :(得分:1)

一组ifelse if...条件组成一个具有单个结果的单个表达式。

在第一个示例中,if else ...链产生nullt2t1。但是if else表达式不是代码块中的唯一表达式,因为if else的结果没有保存在任何地方,所以它被丢弃了,处理移至下一个表达式。这不是您想要或想要的。

第二个代码示例之所以有效,是因为mergeTrees()方法中只有一个表达式,即if else表达式。因此,在那种情况下,if else的结果就是整个方法的结果。

答案 2 :(得分:1)

在Scala语言规范中,以下语法支持def的语法,

Def                ::=  ‘def’ FunDef
FunDef             ::=  FunSig [‘:’ Type] ‘=’ Expr
FunSig             ::=  id [FunTypeParamClause] ParamClauses

因此,在您的def mergeTrees(t1: TreeNode, t2: TreeNode): TreeNode = {...}中,RHS是Expr,以下语法支持Expr的特定语法,

Expr               := Expr1
Expr1              := PostfixExpr
PostfixExpr        := InfixExpr [id [nl]]
InfixExpr          := [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr
SimpleExpr         := BlockExpr
BlockExpr          := ‘{’ Block ‘}’
Block              ::=  BlockStat {semi BlockStat} [ResultExpr]

因此...解析器确定您的RHS为BlockExpr

如果您在-https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#blocks上阅读了Scala语言规范中的blocks,您将会发现以下植入物,

A block expression {s1s1; ……; snsn; ee} is constructed from a sequence of block statements s1,…,sns1,…,sn and a final expression e

现在,block statement中的BlockStat是什么?

我们只考虑以下情况

def abc(i: Int): Int = {
  i + 1
  i + 5
}

在这种情况下,i + 1将被忽略(作为语句),而i + 5将作为返回值。

与您的情况类似(但更简单),然后将RHS BlockExpr解析如下,

'{'
   i + 1 (BlockStat)
   i + 5 (ResultExpr)
'}'

但是,i + 1看起来像Expr(或表达式),然后为什么将其视为语句,这是因为遵循BlockStat的语法,

BlockStat    ::=  Import
               |  {Annotation} [‘implicit’ | ‘lazy’] Def
               |  {Annotation} {LocalModifier} TmplDef
               |  Expr1

这意味着Expr语法中的任何Expr1也可以被视为BlockStat

因此,在这种情况下,由于以下原因,

{
  i + 1
  i + 5
}

与语法最佳匹配

'{'
  BlockStat
  ResultExpr
'}'

因此,实际上是i + 1的{​​{1}}被视为Expr1而不是statement。这意味着它不会成为块表达式的expression,而下一个value将成为块的i + 5

现在,从您以下的value开始,

expression

后跟更多的语句,这些语句将被视为 if (t1 == null && t2 == null) null else if (t1 == null) t2 else if (t2 == null) t1 ,因此不会成为您的statement的返回值。进一步的表达/陈述也将被评估。

既然您的下一条语句是

def

val root = new TreeNode(t1.value + t2.value) t1.value将在评估时抛出NullPointerException,以防t2.valuet1t2的情况。