当前位置:网站首页>如何使用200行代码实现Scala的对象转换器
如何使用200行代码实现Scala的对象转换器
2022-06-27 12:58:00 【梦境迷离】
与CSV转换器类似,但本次所需要处理的类型是不可预见的,所以实现略有不同。
API 定义
trait Transformer[-From, +To] {
def transform(from: From): To
}
object Transformer {
def apply[From, To](implicit st: Transformer[From, To]): Transformer[From, To] = st
}
创建Transformer实例
有API后就是实现API,这可以通过两种方式来实现:
- 使用macro生成。
- 自己new一个。
下面只介绍用宏生成的方式。
使用macro生成
定义宏DSL
object Transformable {
def apply[From <: Product, To <: Product]: Transformable[From, To] =
macro TransformerMacro.applyImpl[From, To]
}
提供setName
和setType
两个方法,分别提供对字段的名称和类型做编译期映射:
/** @author * 梦境迷离 * @version 1.0,6/15/22 */
class Transformable[From, To] {
@unchecked
def setType[FromField, ToField](
selectFromField: From => FromField,
map: FromField => ToField
): Transformable[From, To] =
macro TransformerMacro.mapTypeImpl[From, To, FromField, ToField]
@unchecked
def setName[FromField, ToField](
selectFromField: From => FromField,
selectToField: To => ToField
): Transformable[From, To] =
macro TransformerMacro.mapNameImpl[From, To, FromField, ToField]
def instance: Transformer[From, To] = macro TransformerMacro.instanceImpl[From, To]
}
这么用:
case class A1(a: String, b: Int, cc: Long, d: Option[String])
case class A2(a: String, b: Int, c: Int, d: Option[String])
val a = A1("hello", 1, 2, None)
val b: A2 = Transformable[A1, A2]
.setName(_.cc, _.c)
.setType[Long, Int](_.cc, fromField => if (fromField > 0) fromField.toInt else 0)
.instance
.transform(a)
b.toString shouldEqual "A2(hello,1,2,None)"
原理
- 编译期间存储
setName
和setType
的映射关系,并在类型不兼容时调用映射函数。 - 利用implicit,根据需要,会自动搜索并应用符合要求的
Transformer
隐式值。 setType
不是必须的,因为默认搜索implicit Transformer[From, To]
,如果没有才需要使用setType
,但是如果已经定义了implicit Transformer[From, To]
,则不需要,不过setType
的优先级更高,会覆盖implicit。
超复杂结构:
case class C1(j: Int)
case class D1(
a1: List[List[C1]],
b1: Option[C1],
c1: List[Option[C1]],
d1: Option[List[C1]],
map1: Map[String, String],
intMap1: Map[Int, C1]
)
case class C2(j: Int)
case class D2(
a2: List[List[C2]],
b2: Option[C1],
c2: List[Option[C1]],
d2: Option[List[C1]],
map2: Map[String, String],
intMap2: Map[Int, C2]
)
// NOTE: have collection not support? please implicit val transformer = new Transformer[F, T] { ... }
object C1 {
// 定义了就不需要在下面使用setType方法了
implicit val cTransformer: Transformer[C1, C2] = Transformable[C1, C2].instance
}
object D1 {
implicit val dTransformer: Transformer[D1, D2] = Transformable[D1, D2]
.setName(_.a1, _.a2)
.setName(_.b1, _.b2)
.setName(_.c1, _.c2)
.setName(_.d1, _.d2)
.setName(_.map1, _.map2)
.setName(_.intMap1, _.intMap2)
.instance
}
val d1 = D1(
List(List(C1(1))),
Option(C1(2)),
List(Option(C1(3))),
Option(List(C1(4))),
Map("hello" -> "world"),
Map(1 -> C1(1))
)
val d2: D2 = Transformer[D1, D2].transform(d1)
println(d2)
d2.toString shouldBe "D2(List(List(C2(1))),Some(C1(2)),List(Some(C1(3))),Some(List(C1(4))),Map(hello -> world),Map(1 -> C2(1)))"
这是因为默认提供了常见集合,所以用户端不需要定义这些集合的Transformer
:
implicit final def transformerOption[F, T](implicit e: Transformer[F, T]): Transformer[Option[F], Option[T]] =
new Transformer[Option[F], Option[T]] {
override def transform(from: Option[F]): Option[T] = from.map(e.transform)
}
implicit final def transformerSeq[F, T](implicit e: Transformer[F, T]): Transformer[Seq[F], Seq[T]] =
new Transformer[Seq[F], Seq[T]] {
override def transform(from: Seq[F]): Seq[T] = from.map(e.transform)
}
implicit final def transformerSet[F, T](implicit e: Transformer[F, T]): Transformer[Set[F], Set[T]] =
new Transformer[Set[F], Set[T]] {
override def transform(from: Set[F]): Set[T] = from.map(e.transform)
}
implicit final def transformerList[F, T](implicit e: Transformer[F, T]): Transformer[List[F], List[T]] =
new Transformer[List[F], List[T]] {
override def transform(from: List[F]): List[T] = from.map(e.transform)
}
implicit final def transformerVector[F, T](implicit e: Transformer[F, T]): Transformer[Vector[F], Vector[T]] =
new Transformer[Vector[F], Vector[T]] {
override def transform(from: Vector[F]): Vector[T] = from.map(e.transform)
}
implicit final def transformerMap[K, F, T](implicit
e: Transformer[F, T]
): Transformer[Map[K, F], Map[K, T]] =
new Transformer[Map[K, F], Map[K, T]] {
override def transform(from: Map[K, F]): Map[K, T] = from.map(kv => kv._1 -> e.transform(kv._2))
}
implicit final def transformerSeqList[F, T](implicit e: Transformer[F, T]): Transformer[Seq[F], List[T]] =
new Transformer[Seq[F], List[T]] {
override def transform(from: Seq[F]): List[T] = from.map(e.transform).toList
}
implicit final def transformerListSeq[F, T](implicit e: Transformer[F, T]): Transformer[List[F], Seq[T]] =
new Transformer[List[F], Seq[T]] {
override def transform(from: List[F]): Seq[T] = from.map(e.transform)
}
边栏推荐
猜你喜欢
Tiktok practice ~ public / private short video interchange
Two TCP flow control problems
IJCAI 2022 | greatly improve the effect of zero sample learning method with one line of code. Nanjing Institute of Technology & Oxford proposed the plug and play classifier module
Make learning pointer easier (2)
TCP 流控问题两则
Three traversal methods of binary tree
Teach you how to build a permanent personal server!
Journal quotidien des questions (6)
Openfeign service interface call
【周赛复盘】LeetCode第81场双周赛
随机推荐
每日刷题记录 (六)
Cool in summer
After the deployment is created, the pod problem handling cannot be created
[medical segmentation] unet3+
【医学分割】unet3+
Journal quotidien des questions (6)
Implementation of recruitment website based on SSM
防火墙基础之华为华三防火墙web页面登录
Ali an interview question: use two threads to output letters and numbers alternately
Database Series: MySQL index optimization and performance improvement summary (comprehensive version)
ensp云朵配置
ZABBIX supports nail alarm
【TcaplusDB知识库】TcaplusDB-tcapsvrmgr工具介绍(三)
[weekly replay] the 81st biweekly match of leetcode
Yuweng information, a well-known information security manufacturer, joined the dragon lizard community to build an open source ecosystem
阿胖的操作记录
On the complexity of software development and the way to improve its efficiency
OpenFeign服务接口调用
crane:字典项与关联数据处理的新思路
7 killer JS lines of code