当前位置:网站首页>如何使用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)
}
边栏推荐
- Cool in summer
- 基于STM32设计的蓝牙健康管理设备
- ensp云朵配置
- 不一样的习惯
- An interesting experiment of netmask
- [medical segmentation] unet3+
- 【周赛复盘】LeetCode第81场双周赛
- 【TcaplusDB知识库】TcaplusDB-tcapsvrmgr工具介绍(三)
- Pre training weekly issue 51: reconstruction pre training, zero sample automatic fine tuning, one click call opt
- [acwing] explanation of the 57th weekly competition
猜你喜欢
![[acwing] explanation of the 57th weekly competition](/img/ef/be89606b0e7fffac08280db0a73781.gif)
[acwing] explanation of the 57th weekly competition

每日刷題記錄 (六)

IJCAI 2022 | 用一行代码大幅提升零样本学习方法效果,南京理工&牛津提出即插即用分类器模块

Does Xinhua San still have to rely on ICT to realize its 100 billion enterprise dream?

A pang's operation record

Summary of redis master-slave replication principle

Hue new account error reporting solution

Quick news: Huawei launched the Hongmeng developer competition; Tencent conference released the "Wanshi Ruyi" plan
![[medical segmentation] unet3+](/img/93/1e9728a3dbebbf3bd9ce015552ce7a.jpg)
[medical segmentation] unet3+

Hardware development notes (VII): basic process of hardware development, making a USB to RS232 module (VI): creating 0603 package and associating principle graphic devices
随机推荐
[medical segmentation] unet3+
On the complexity of software development and the way to improve its efficiency
【第27天】给定一个整数 n ,打印出1到n的全排列 | 全排列模板
Differences in perspectives of thinking
Good luck today
Details of istio micro service governance grid traffic management core resource controller
What kind of air conditioner is this?
Three traversal methods of binary tree
Prometheus 2.26.0 新特性
[weekly replay] the 81st biweekly match of leetcode
【动态规划】—— 背包问题
Centos7 command line installation Oracle11g
ACL 2022 | TAMT proposed by Chinese Academy of Sciences: TAMT: search for a portable Bert subnet through downstream task independent mask training
Make learning pointer easier (2)
如何修改 node_modules 里的文件
诗歌一首看看
【医学分割】unet3+
《预训练周刊》第51期:重构预训练、零样本自动微调、一键调用OPT
ViewPager2使用记录
执行 npm 指令失败,提示ssh: ... Permission denied