晚上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)
: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))))
个人觉得这种情况下编译器报错似乎更合适。