Я попробовал неявные преобразования в следующем примере:
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
Почему мы не можем применять неявные преобразования к ключам карты? Есть ли способ обойти это?
Ответ 1 · рейтинг 5 · slouc
Это не работает, потому что вы используете ->, который является (встроенным) оператором:
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)
Ответ 2 · рейтинг 2 · Jörg W Mittag
Пожалуйста, посмотрите на сообщение об ошибке, которое вы получаете:
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)
Вуаля! Оно работает.
Ответ 3 · рейтинг 1 · radumanolescu
Если бы вы добавили такое общее неявное преобразование, вы бы потеряли безопасность типов, которую обеспечивает Scala, потому что любая String стала бы Int по мере необходимости, где угодно, без вмешательства программиста. На самом деле, когда вы хотите создать эту карту из других данных, вы, вероятно, уже знаете типы данных этих других данных. Итак, если известно, что ключи являются целыми числами, преобразуйте их в Int и используйте их таким образом. В противном случае используйте строки. Ваш пример очень искусственный. Какую конкретную проблему вы пытаетесь решить?