Как я могу получить неявные преобразования для работы внутри коллекций?
Скажем, что у меня есть неявное преобразование:
implicit def aToB(a: A):B={
...
}
Как я могу заставить это неявное преобразование работать с элементами списка?
Если у меня есть:
val listOfA: List[A] ...
и у меня есть функция, которая принимает список из B, можно ли позволить Scala неявно преобразовать все элементы из A в B?
Без неявных преобразований преобразование может выглядеть так:
lisftOfA.map(a => new B(a.someValue, a.anotherValue))
Но я бы хотел, чтобы это произошло как "волшебство"... это слишком много, чтобы спросить.
Ответы
Ответ 1
Вот несколько альтернатив, которые вы, возможно, пожелаете рассмотреть:
1. Используйте привязку вида
Если возможно изменить функцию, которая принимает список Bs, это было бы самым простым решением. Измените его, чтобы принять список вещей, которые можно преобразовать в Bs. То есть
def yourFn(l: List[B]) = ...
станет
def yourFn[X <% B](l: List[X]) = ...
Затем вы можете просто вызвать функцию с помощью listOfA:
yourFn(listOfA)
2. Ввести метод преобразования
Это похоже на первое решение Rogach, за исключением того, что внешнее преобразование неявно:
def convert[B, A <% B](l: List[A]): List[B] = l map { a => a: B }
Затем на вашей функции call-site вы должны написать
yourFn(convert(listOfA))
Как и второе решение Rogach, это немного безопаснее, чем приведение неявного преобразования.
3. Внесите неявное преобразование
Это эквивалентно первому решению Rogach, но нотация немного лучше (IMO).
implicit def convert[B, A <% B](l: List[A]): List[B] = l map { a => a: B }
Если это преобразование находится в области на вашем сайте вызова, вы можете просто вызвать свою функцию с помощью listOfA:
yourFn(listOfA)
Размышления о мыслях
Интересно рассмотреть, как решить эту проблему в общем виде. Что делать, если я хочу определить свой метод преобразования, чтобы он мог обрабатывать любой тип, реализующий метод map
? т.е.
def convert[B, A <% B, C[_]](c: C[A]): C[B] = c map { a => a: B }
Это не сработает, конечно, поскольку ничего в сигнатуре не выражает ограничение, которое C
должно реализовать map
. Насколько я знаю, выражение этого ограничения довольно активно и не может быть выполнено таким образом, чтобы обеспечить автономную поддержку любого типа, реализующего map
. См. Понятия типа Scala.
Ответ 2
Ниже приведено общее решение, обеспечивающее неявное преобразование списков (List [A] = > List [B]), если неявное преобразование A = > B доступно в области видимости:
scala> class A
defined class A
scala> class B
defined class B
scala> implicit def a2b(a:A) = new B
a2b: (a: A)B
scala> implicit def mapI[A,B](l: List[A])(implicit conv: A => B): List[B] = l.map(conv)
mapI: [A, B](l: List[A])(implicit conv: (A) => B)List[B]
scala> List(new A): List[B]
res0: List[B] = List([email protected])
Это то, что вам нужно?
Кроме того, поскольку у вас уже есть это неявное преобразование, вы можете просто написать:
listOfA.map(a2b) // List[B]
Это будет немного более подробным, но дает вам более четкое управление вашим кодом.
Ответ 3
Я думаю, что лучшее, что возможно, это
implicit def asToBs(as: List[A]): List[B] = as map aToB
Ответ 4
Для полноты вы можете рассмотреть более общий подход, используя CanBuildFrom
.
Эта программа:
import scala.collection.generic.CanBuildFrom
import scala.language.{higherKinds, implicitConversions}
implicit def implyConvertedTraversable[A, B, C[X] <: Traversable[X]](as: C[A])(implicit conversion: A => B, cbf: CanBuildFrom[C[A], B, C[B]]): C[B] = {
val builder = cbf(as)
builder.sizeHint(as)
builder ++= as.map(conversion)
builder.result()
}
implicit def implyString(a: Int): String = a.toString
val intList = List(1, 2, 3)
val intStream = Stream(1, 2, 3)
val stringList: List[String] = intList
val stringStream: Stream[String] = intStream
Урожайность:
intList: List[Int] = List(1, 2, 3)
intStream: scala.collection.immutable.Stream[Int] = Stream(1, ?)
stringList: List[String] = List(1, 2, 3)
stringStream: Stream[String] = Stream(1, ?)