对地图应用隐式转换
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)
我在以下示例中尝试了隐式转换:
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)