Scala Map 的获取与应用操作:"type mismatch"
Scala Map's get vs apply operation: "type mismatch"
我正在学习 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
The value associated with key k in map ms as an option, None if not found.
ms(k)
(or, written out, ms apply k
)
The value associated with key k in map ms, or exception if not found.
为什么第三行不起作用?
与signature略有不同:
abstract def get(key: K): Option[V]
def apply(key: K): V
问题是错误处理:get
将在未找到元素时 return 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
,添加似乎是最近的。
它本质上只是隐式参数与 apply
-语法糖和奇怪的括号-消除行为的特殊冲突。
如explained here,
中的括号
(List(('a', 1)).toMap)('a')
被过早丢弃,所以你最终得到
List(('a', 1)).toMap('a')
以便编译器尝试将 'a'
解释为某些未知类型 ?
、?
.
的隐含证据 (Char, Int) <:< (?, ?)
这在这里有效(它没有用,它只是为了演示编译器通常在这个位置期望什么):
(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)
我认为在这一点上应该很明显为什么 .get
表现不同,所以我不会详细说明。
我正在学习 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
The value associated with key k in map ms as an option, None if not found.
ms(k)
(or, written out,ms apply k
)The value associated with key k in map ms, or exception if not found.
为什么第三行不起作用?
与signature略有不同:
abstract def get(key: K): Option[V]
def apply(key: K): V
问题是错误处理:get
将在未找到元素时 return 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
,添加似乎是最近的。
它本质上只是隐式参数与 apply
-语法糖和奇怪的括号-消除行为的特殊冲突。
如explained here,
中的括号(List(('a', 1)).toMap)('a')
被过早丢弃,所以你最终得到
List(('a', 1)).toMap('a')
以便编译器尝试将 'a'
解释为某些未知类型 ?
、?
.
(Char, Int) <:< (?, ?)
这在这里有效(它没有用,它只是为了演示编译器通常在这个位置期望什么):
(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)
我认为在这一点上应该很明显为什么 .get
表现不同,所以我不会详细说明。