Scala Map的获取与应用操作:“类型不匹配”

时间:2019-08-06 16:28:56

标签: scala implicit syntactic-sugar

我正在学习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关联的值;如果未找到,则为异常。

为什么第三行不起作用?

2 个答案:

答案 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,添加的内容似乎是最近的。