我在以下示例中尝试了隐式转换:
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
为什么我们无法将隐式转换应用于地图键?有办法解决这个问题吗?
答案 0 :(得分:5)
它不起作用,因为您使用的是->
(内联)运算符:
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已经“固定”。我们只是说,在使用->
运算符时,您只能(隐式)转换元组的右侧:
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)
答案 1 :(得分:2)
请查看您收到的错误消息:
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)
瞧!它有效。
答案 2 :(得分:1)
如果你要添加这样一般的隐式转换,你将失去Scala强制执行的类型安全性,因为任何String都可以在任何地方根据需要成为Int,而无需程序员的干预。 实际上,当您想要从其他数据创建该地图时,您可能已经知道该其他数据的数据类型。因此,如果已知键是整数,则将它们转换为Int并使用它们。否则,请使用字符串。 你的例子非常人为。你试图解决哪个具体问题?