我正在学习Scala,发现了以下内容:
List(('a', 1)).toMap get 'a' // Option[Int] = Some(1)
(List(('a', 1)).toMap) apply 'a' // Int = 1
(List(('a', 1)).toMap)('a') // Error: type mismatch;
found : Char('a')
required: <:<[(Char, Int),(?, ?)
(List(('a', 1)).toMap)('a')
但是再次将其分配给变量即可。
val b = (List(('a', 1)).toMap)
b('a') // Int = 1
为什么会这样?
标准文档提供:
ms get k
与地图ms中的键k关联的值是一个选项,如果找不到,则为None。
ms(k)
(或写成ms apply k
)与映射ms中的键k关联的值;如果未找到,则为异常。
为什么第三行不起作用?
答案 0 :(得分:4)
从本质上讲,这只是隐式参数与apply
-语法糖和奇怪的括号消除行为的特质碰撞。
在explained here中,括号位于
(List(('a', 1)).toMap)('a')
丢弃得太早了,所以最终得到
List(('a', 1)).toMap('a')
,以便编译器尝试将'a'
解释为某些未知类型(Char, Int) <:< (?, ?)
,?
的{{1}}的隐含证据。
这在这里有效(它没有用,只是为了演示编译器在该位置通常会期望什么):
?
将(List(('a', 1)).toMap(implicitly[(Char, Int) <:< (Char, Int)]))('a')
分配给变量也可以:
List(...).toMap
或者,您可以通过将({val l = List((1, 2)).toMap; l})(1)
馈给不执行任何操作的toMap
函数来强制停止接受参数:
identity
但是消除隐式参数和identity(List((1, 2)).toMap)(1)
-语法糖的歧义的最简单,最清晰的方法就是明确地写出apply
:
.apply
我认为在这一点上,List((1, 2)).toMap.apply(1)
表现不同的原因很明显,因此我不再赘述。
答案 1 :(得分:1)
signature略有不同:
abstract def get(key: K): Option[V]
def apply(key: K): V
问题在于错误处理:找不到元素时get
将返回None
,而apply
将引发异常:
scala> Map(1 -> 2).get(3)
res0: Option[Int] = None
scala> Map(1 -> 2).apply(3)
java.util.NoSuchElementException: key not found: 3
at scala.collection.immutable.Map$Map1.apply(Map.scala:111)
... 36 elided
关于失败行:toMap有一个隐式参数ev: A <:< (K,V)
,表示类型约束。调用r.toMap('a')
时,您将为隐式传递一个显式值,但类型错误。 Scala 2.13.0有一个伴随对象<:<
,它提供了一种反射方法(使用给定的类型本身而不是适当的子类型)。现在有以下作品:
scala> List(('a', 1)).toMap(<:<.refl)('a')
res3: Int = 1
备注:我无法在Scala 2.12.7中调用<:<.refl
,添加的内容似乎是最近的。