对地图应用隐式转换

Applying implicit conversion to map

我在以下示例中尝试了隐式转换:

val m: Map[Int, Int] = Map(10 -> "asd")  //fine
val mm: Map[Int, Int] = Map("asd" -> 20) //type mismatch; found: (String, Int) 
                                         //required: (Int, Int)

implicit def stringToInt(str: String): Int = 10

为什么我们不能对映射键应用隐式转换?有办法解决这个问题吗?

如果您要添加这样一个通用的隐式转换,您将失去 Scala 强制执行的类型安全性,因为任何 String 都会根据需要在任何地方变成 Int,而无需程序员的干预。 实际上,当您想从其他数据创建地图时,您可能已经知道其他数据的数据类型。因此,如果已知键是整数,请将它们转换为 Int 并像那样使用它们。否则,使用字符串。 你的例子是高度人为的。您要解决哪个具体问题?

请查看您收到的错误消息:

error: type mismatch;
found   : (String, Int)
required: (Int, Int)
      val mm: Map[Int, Int] = Map("asd" -> 20)
                                        ^

错误信息是不是关于String而不是Int,它是关于(String, Int)而不是(Int, Int)。所以,你只是在转换错误的东西:

implicit def tupleIntifier[T](t: (String, T)) = (10, t._2)

val mm: Map[Int, Int] = Map("asd" -> 20)
//=> mm: Map[Int,Int] = Map(10 -> 20)

瞧!有效。

它不起作用,因为您使用的是 ->,它是一个(内联)运算符:

implicit final class ArrowAssoc[A](self : A) extends scala.AnyVal {
  @scala.inline
  def ->[B](y : B) : scala.Tuple2[A, B] = { /* compiled code */ }
  def →[B](y : B) : scala.Tuple2[A, B] = { /* compiled code */ }
}

您可以看到,在评估 B 时,A 已经 "fixed"。假设您只能(隐含地)在使用 -> 运算符时转换元组的右侧:

implicit def stringToInt(str: String): Int = 10  
implicit def intToStr(str: Int): String = "a"

val a: Map[Int, Int] = Map(10 -> "asd") //fine
val b: Map[Int, Int] = Map("asd" -> 20) // error! cant change left side

val c: Map[String, String] = Map("asd" -> 20) // fine 
val d: Map[String, String] = Map(10 -> "asd") // error! cant change left side

由于与使用运算符 -> 相关的类似编译器怪癖,@Jorg 的解决方案在一个方向上有效,但在另一个方向上无效:

implicit def tupleIntifier(t: (String, Int)) = (10, 10)
implicit def tupleIntifier2(t: (Int, String)) = (10, 10)

val a: Map[Int, Int] = Map("asd" -> 20) // uses tupleIntifier
val b: Map[Int, Int] = Map(10 -> "asd") // fails!!

但是,如果您完全避免使用 -> 运算符并仅使用 (key, value) 语法,它将起作用:

val a: Map[Int, Int] = Map((10, "asd"))
val b: Map[Int, Int] = Map(("asd", 20))

implicit def stringToInt(str: String): Int = 15

println(a) // prints Map(10 -> 15)
println(b) // prints Map(15 -> 20)