繁体   English   中英

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

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

说我有一个隐含的转换:

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

如何才能将此隐式转换用于List的元素?

如果我有:

val listOfA: List[A] ...

我有一个带B列表的函数,是否可以让Scala隐式地将所有元素从A转换为B?

如果没有隐式转换,转换可能如下所示:

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

但是,我希望这种情况像'魔术'一样发生......这太难以问了。

以下是您可能希望考虑的一些替代方案:

1.使用视图绑定

如果可以更改采用B列表的函数,这将是最简单的解决方案。 修改它以接受可以转换为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 }

然后在你的函数的调用站点,你会写

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序列推导

以下是一般解决方案,如果隐式转换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)

这是你需要的吗?

此外,既然你已经有了隐式转换,你可以写:

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

它会更冗长,但会让您更加明确地控制代码。

我认为最好的可能是

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

为了完整CanBuildFrom ,您可能希望考虑使用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, ?)

暂无
暂无

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

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