简体   繁体   English

如何在集合内部进行隐式转换?

[英]How can I get implicit conversions to work inside collections?

Say I have an implicit conversion: 说我有一个隐含的转换:

implicit def aToB(a: A):B={
...
}

How can I get this implicit conversion to work on the elements of a List? 如何才能将此隐式转换用于List的元素?

If I have: 如果我有:

val listOfA: List[A] ...

and I have a function that takes a List of B, is it possible to let Scala implicitly convert all of the elements from A's to B's? 我有一个带B列表的函数,是否可以让Scala隐式地将所有元素从A转换为B?

Without implicit conversions, the conversion might look like: 如果没有隐式转换,转换可能如下所示:

lisftOfA.map(a => new B(a.someValue, a.anotherValue))

But I would love for this to happen like 'magic'... is that too much to ask. 但是,我希望这种情况像'魔术'一样发生......这太难以问了。

Here are a few alternatives you might wish to consider: 以下是您可能希望考虑的一些替代方案:

1. Use a view bound 1.使用视图绑定

If it's possible to change the function that takes a List of Bs, this would be the simplest solution. 如果可以更改采用B列表的函数,这将是最简单的解决方案。 Modify it to accept a List of things that can be converted to Bs. 修改它以接受可以转换为Bs的事物列表。 That is, 那是,

def yourFn(l: List[B]) = ...

would become 会成为

def yourFn[X <% B](l: List[X]) = ...

Then, you can just call the function with listOfA: 然后,您可以使用listOfA调用该函数:

yourFn(listOfA)

2. Introduce a conversion method 2.介绍一种转换方法

This is similar to Rogach's first solution, except that the outer conversion is non-implicit: 这与Rogach的第一个解决方案类似,只是外部转换是非隐式的:

def convert[B, A <% B](l: List[A]): List[B] = l map { a => a: B }

Then at your function's call-site, you would write 然后在你的函数的调用站点,你会写

yourFn(convert(listOfA))

Like Rogach's second solution, this is bit safer than bringing in an implicit conversion. 与Rogach的第二个解决方案一样,这比引入隐式转换更安全一些。

3. Introduce an implicit conversion 3.引入隐式转换

This is equivalent to Rogach's first solution, but the notation is a bit nicer (IMO). 这相当于Rogach的第一个解决方案,但符号更好(IMO)。

implicit def convert[B, A <% B](l: List[A]): List[B] = l map { a => a: B }

If this conversion is in scope at your call-site, you can just call your function with the listOfA: 如果此转换在您的呼叫站点的范围内,您可以使用listOfA调用您的函数:

yourFn(listOfA)

Parting Thoughts 分手思考

It's interesting to consider how to solve this problem in a general way. 考虑如何以一般方式解决这个问题很有意思。 What if I want to define my conversion method so that it can handle any type that implements the map method? 如果我想定义我的转换方法以便它可以处理任何实现map方法的类型,该怎么办? ie,

def convert[B, A <% B, C[_]](c: C[A]): C[B] = c map { a => a: B }

This won't work, of course, since nothing in the signature expresses the constraint that C must implement map . 当然,这不起作用,因为签名中没有任何内容表示C必须实现map的约束。 As far as I know, expressing this constraint is fairly involved and cannot be done in a way that provides out-of-the-box support for any type that implements map . 据我所知,表达这种约束是相当复杂的,不能以一种为实现map任何类型提供开箱即用支持的方式来完成。 See Type-safe Scala sequence comprehensions . 请参阅类型安全的Scala序列推导

The following is a general solution, enabling implicit conversion of lists (List[A] => List[B]) if implicit conversion A=>B is available in scope: 以下是一般解决方案,如果隐式转换A => B在范围内可用,则启用列表的隐式转换(List [A] => List [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(B@efa0bf4)

Is this what you need? 这是你需要的吗?

Also, since you already have that implicit conversion, you can just write: 此外,既然你已经有了隐式转换,你可以写:

listOfA.map(a2b) // List[B]

It would be a bit more verbose, but gives you a bit more explicit control of your code. 它会更冗长,但会让您更加明确地控制代码。

我认为最好的可能是

implicit def asToBs(as: List[A]): List[B] = as map aToB

For completeness, you may wish to consider a more generic approach employing CanBuildFrom . 为了完整CanBuildFrom ,您可能希望考虑使用CanBuildFrom的更通用的方法。

This program: 这个程序:

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

Yields: 产量:

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, ?)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM