将值附加到MAP时,为什么Scala需要额外的括号块才能使此语句有效?
不编译:
vmap += (item.getName(), item.getString()) // compiler output: "found: String"
但是,这会编译:
vmap += ((item.getName(), item.getString())) // note the second set of enclosures
TIA
编辑:vmap定义为
val vmap = new mutable.HashMap[String, String]()
尾声:
在编辑时,有一些帖子详细说明了两种可能的解释,这两种解释似乎都含有真实的元素。哪一个实际上是正确的?我无法肯定地说...我只是一个仍在学习语言的人。话虽这么说,我已经改变了基于感觉的答案选择,一个答案(至少在某种程度上)包含在另一个答案中 - 所以我选择了更大的画面,因为我认为它将为其他人寻找答案提供更广泛的意义。具有讽刺意味的是,我试图更好地理解如何消除语言中的一些细微差别,而我已经意识到的是,有更多的东西比我怀疑的更多。我并不是说这是一件坏事 - 实际上(IMO)可以从任何语言中获得灵活和复杂的语言 - 但它肯定会让一个人错过黑/白的世界不时组装......
为了得出结论,有几点意见:
1)所选答案包含一个链接到一个充满Scala脑筋的网站(我发现这非常有助于理解该语言中的一些上述夸克。)强烈推荐。
2)我确实遇到了另一个有趣的转折 - 而单括号(上面的例子)不起作用,改变它以下它并且工作得很好......
vmap += ("foo" -> "bar")
这可能与匹配方法/功能签名有关,但这只是我的猜测。
答案 0 :(得分:7)
接受的答案实际上是错误的。
你没有得到Map。+ =的元组的原因是该方法被第二个带有两个或更多args的方法重载。
如果args的数量错误,编译器将只尝试使用tupling。但是如果你给它两个args,并且有一个方法需要两个,这就是它所选择的,即使它没有键入check。
在某些事情发生之前,它并没有开始尝试所有可能的组合,因为那将充满默默无闻。 (Cf暗示。)
scala> def f(p: Pair[String, Int]) = { val (s,i) = p; s.toInt + i }
f: (p: Pair[String,Int])Int
scala> f("2",3) // ok to tuple
res0: Int = 5
scala> trait F { def +=(p: Pair[String, Int]) = f(p) }
defined trait F
scala> val foo = new F {}
foo: F = $anon$1@6bc77f62
scala> foo += ("2",3) // ok to tuple
res1: Int = 5
scala> trait G { def +=(p: Pair[String, Int]) = f(p); def +=(p:(String,Int),q:(String,Int),r:(String,Int)*) = f(p)+f(q)+(r map f).sum }
defined trait G
scala> val goo = new G {}
goo: G = $anon$1@183aeac3
scala> goo += ("2",3) // sorry
<console>:12: error: type mismatch;
found : String("2")
required: (String, Int)
goo += ("2",3)
^
scala> goo += (("2",3),("4",5),("6",7))
res3: Int = 27
我不得不提及你的朋友和我的,-Xlint,它会警告不适当的arg改编:
apm@mara:~/tmp$ skala -Xlint
Welcome to Scala version 2.11.0-20130811-132927-95a4d6e987 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def f(p: Pair[String, Int]) = { val (s,i) = p; s.toInt + i }
f: (p: Pair[String,Int])Int
scala> f("2",3)
<console>:9: warning: Adapting argument list by creating a 2-tuple: this may not be what you want.
signature: f(p: Pair[String,Int]): Int
given arguments: "2", 3
after adaptation: f(("2", 3): (String, Int))
f("2",3)
^
res0: Int = 5
scala> List(1).toSet()
<console>:8: warning: Adapting argument list by inserting (): this is unlikely to be what you want.
signature: GenSetLike.apply(elem: A): Boolean
given arguments: <none>
after adaptation: GenSetLike((): Unit)
List(1).toSet()
^
res3: Boolean = false
关于适应的危险,请参阅the Adaptive Reasoning puzzler和this new one that is pretty common,因为我们了解到parens的存在与否在很大程度上取决于风格,当parens真正重要时,使用它们会产生错误的结果。类型错误。
在存在重载的情况下调整元组:
scala> class Foo {
| def f[A](a: A) = 1 // A can be (Int,Int,Int)
| def f[A](a: A, a2: A) = 2
| }
defined class Foo
scala> val foo = new Foo
foo: Foo = Foo@2645d22d
scala> foo.f(0,0,0)
<console>:10: warning: Adapting argument list by creating a 3-tuple: this may not be what you want.
signature: Foo.f[A](a: A): Int
given arguments: 0, 0, 0
after adaptation: Foo.f((0, 0, 0): (Int, Int, Int))
foo.f(0,0,0)
^
res9: Int = 1
答案 1 :(得分:3)
因为Map +=
方法将Tuple2[A, B]
作为参数。
您必须使用周围的括号标记Tuple2[A, B]
,否则编译器不会将类型推断为Tuple2
。
Tuple2
是一对简单的A - &gt;乙
val x = (5, 7); // the type is inferred to Tuple2[Int, Int];
Map
迭代使这更加明显:
map.foreach { case (key, value) => ...};
(key, value)// is a Tuple2
第一组封闭括号被编译器视为无效/可跳过。
第二组括号括起来创建所需的Tuple2
。
val x = (item.getName(), item.getString());//Tuple2[String, String]
vmap += x; // THIS COMPILES
vmap += (item.getName(), item.getString())// is identical to
vmap += item.getName(), item.getString() // now it thinks you are adding a String, the result of item.getName()
vmap += ( (item.getName(), item.getString()) )// skips the first set, sees the Tuple, compiles.
来自SLS:
后缀运算符的优先级低于中缀运算符, 所以foo bar baz = foo.bar(baz)
在这种情况下:vmap += (item.getName(), item.getString()); = vmap.+=(item.getName());