scala雾中风景(22): var变量与赋值操作符

晚上review代码时发现的一个问题,同事写的一段代码简化后如下:

var map = scala.collection.immutable.Map[Int,Int]()
map += (1->2)
map += (3->4)
...
if (xxx) map.get(..)

这里逻辑上感觉有问题,map这里用的不可变集合,+=方法应该是返回一个新的值,后边的get应该不能生效。另外觉得 += 这个方法似乎应该只在mutable集合下才有,试图在eclipse下点击进入这个+=方法看看,果然也无法进入。

因为代码上下文逻辑较多,不确定是不是上下文有影响,于是在repl下验证一下这个方法。

scala> val m = scala.collection.immutable.Map[Int,Int]()

scala> m += (1->1)
<console>:12: error: value += is not a member of scala.collection.immutable.Map[Int,Int]
          m += (1->1)
            ^

提示说Map没有这个方法,奇怪为何那段代码在eclipse编译没有问题。然后注意到同事用的是var修饰的map

修改为var之后可以使用+=了,看一下编译器怎么翻译的:

scala> import reflect.runtime.universe._

scala> var m = Map[Int,Int]()

scala> reify ( m += (1->1) )
res4: reflect.runtime.universe.Expr[Unit] = 
        Expr[Unit]($read.m_$eq($read.m.$plus(Predef.ArrowAssoc(1).$minus$greater(1))))

原来这里+=是赋值操作符(assignment operator)而不是方法,恍然大悟,在scala下赋值操作符仅对var修饰的变量生效。m += (k->v)相当于 m = m + (k->v)Map里正好定义了+方法:

def + [B1 >: B](kv: (A, B1)): Map[A, B1]

如果没有定义过+方法的话,编译器会采用字符串连接,也就是对+前后的对象进行toString然后拼接;这种情况下会因为类型不匹配而报错。

所以他的代码也能work,只是这里使用+=操作符容易误会为是方法。建议还是采用val修饰一个可变的Map,来存放数据。

补充,对于var变量,即使以.+=方式调用,编译器也会对待为操作符:

scala> reify ( m.+=(1->1) )
res11: reflect.runtime.universe.Expr[Unit] = 
        Expr[Unit]($read.m_$eq($read.m.$plus(Predef.ArrowAssoc(1).$minus$greater(1))))

个人觉得这种情况下编译器报错似乎更合适。

scala雾中风景(22): var变量与赋值操作符》上有5条评论

  1. hongjiang 文章作者

    scala里支持的操作符:http://www.tutorialspoint.com/scala/scala_operators.htm

    回复
  2. Kabie

    这个还是要看场合吧……

    毕竟牵扯到并发要考虑能不能用mutable

    回复
  3. Edward

    immutable.Map 在var声明下支持 += 操作在 Programming in Scala 17.3 Selecting mutable versus immutable collections 中将到过, 但是 m += (k->v)相当于 m = m + (k->v) 还是看到你的文章才认识的, 多谢。
    现在我正在学习Scala,非常喜欢Scala和Java, 看到前辈翻译的Twitter的Effective Scala, 肃然起敬啊, 希望以后能达到前辈的水平。

    回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注