Case 类 - 使用转换复制多个字段
Case classes - Copy multiple fields with transforms
我有一个包含 25 个字段的案例 class,需要将其转换为另一个包含 22 个字段的案例,其中 19 个是共享的,另外 3 个只是重命名。
我找到了一些如何使用 shapeless
(例如 an answer here and some code examples from Miles Sabin here and here)执行此操作的示例,但最后一个看起来有点过时了,我无法从Github 示例如何使用 shapeless 重命名多个字段,或者在将字段添加到新对象之前对字段进行更多操作。谁能帮帮我?
简化的代码示例;
import shapeless.LabelledGeneric
case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)
val aGen = LabelledGeneric[A]
val bGen = LabelledGeneric[B]
val freddie = new A(1,"Freddie","somestring")
val record = aGen.to(freddie)
val atmp = freddie.fieldA
record.Remove("fielda")
val freddieB = bGen.from(record +
(Symbol("fieldARenamed") ->> atmp) +
(Symbol("fieldCTransformed") ->> freddie.fieldC.toUpperCase)
) //Errors everywhere, even if I replace + with :: etc.
我有一种感觉 Align
会出现在这里的某个地方,但了解如何以最精简的方式做到这一点 - 例如如果不创建像 Field
这样的额外特征,就像上面第三个 link 那样 - 会很有趣。
在The Shapeless Guide中也有一些单引号的用法,(例如'fieldC
)表示法,我没能找到太多相关信息,所以如果它起到角色一些解释也将非常有帮助。对于 Scala 魔法的这种深度来说,这是相当新的,如果问题看起来很迟钝或涉及太多不同的主题,我们深表歉意。
编辑:为避免疑义,我 不是 寻找建议我只是通过引用第一个字段手动创建新案例 class 的答案, 如;
val freddieB = B(fieldARenamed = freddie.fieldA, fieldB = freddie.fieldB, fieldC = freddie.fieldC, fieldCTransformed =freddie.fieldC.toUpperCase)
请参阅下面的评论,了解为什么这是不合适的。
最简单的解决方案是使用旧实例的值构造新实例 case class
,并根据需要对这些值应用函数。代码将非常高效,代码的目的将非常明确,编写它比任何其他解决方案花费的时间都更少,它比依赖于 third-party 库的解决方案更健壮和可维护,并且它避免了两者之间隐藏的依赖关系 类.
另一种选择是使用 automapper; in particular, Dynamic Mappings 功能。
对于您的特定示例,它看起来如下所示:
import io.bfil.automapper._
case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)
val freddie = new A(1,"Freddie","somestring")
val freddieB = automap(freddie).dynamicallyTo[B](
fieldARenamed = freddie.fieldA,
fieldCTransformed = freddie.fieldC.toUpperCase
)
我想你可以把它变成一个函数
def atob(a: A): B = {
automap(a).dynamicallyTo[B](
fieldARenamed = a.fieldA,
fieldCTransformed = a.fieldC.toUpperCase
)
}
从效率的角度来看,这个库使用了宏,所以generated code几乎和手写的一样好
仅供参考,这是让您的问题代码起作用的一种方法。
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{Align,Intersection}
import shapeless.syntax.singleton._
case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)
val fromGen = LabelledGeneric[A]
val toGen = LabelledGeneric[B]
val freddie = A(1, "Freddie", "somestring")
val putARename = Symbol("fieldARenamed") ->> freddie.fieldA
val putCTrans = Symbol("fieldCTransformed") ->> freddie.fieldC.toUpperCase
trait Field { type K; type V; type F = FieldType[K, V] }
object Field {
def apply[K0,V0](sample: FieldType[K0,V0]) =
new Field { type K = K0; type V = V0 }
}
val pFieldA = Field(putARename)
val pFieldCT = Field(putCTrans)
val inter = Intersection[pFieldA.F :: pFieldCT.F :: fromGen.Repr, toGen.Repr]
val align = Align[inter.Out, toGen.Repr]
toGen.from(align(inter(putARename :: putCTrans :: fromGen.to(freddie))))
//res0: B = B(1,Freddie,somestring,SOMESTRING)
我有一个包含 25 个字段的案例 class,需要将其转换为另一个包含 22 个字段的案例,其中 19 个是共享的,另外 3 个只是重命名。
我找到了一些如何使用 shapeless
(例如 an answer here and some code examples from Miles Sabin here and here)执行此操作的示例,但最后一个看起来有点过时了,我无法从Github 示例如何使用 shapeless 重命名多个字段,或者在将字段添加到新对象之前对字段进行更多操作。谁能帮帮我?
简化的代码示例;
import shapeless.LabelledGeneric
case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)
val aGen = LabelledGeneric[A]
val bGen = LabelledGeneric[B]
val freddie = new A(1,"Freddie","somestring")
val record = aGen.to(freddie)
val atmp = freddie.fieldA
record.Remove("fielda")
val freddieB = bGen.from(record +
(Symbol("fieldARenamed") ->> atmp) +
(Symbol("fieldCTransformed") ->> freddie.fieldC.toUpperCase)
) //Errors everywhere, even if I replace + with :: etc.
我有一种感觉 Align
会出现在这里的某个地方,但了解如何以最精简的方式做到这一点 - 例如如果不创建像 Field
这样的额外特征,就像上面第三个 link 那样 - 会很有趣。
在The Shapeless Guide中也有一些单引号的用法,(例如'fieldC
)表示法,我没能找到太多相关信息,所以如果它起到角色一些解释也将非常有帮助。对于 Scala 魔法的这种深度来说,这是相当新的,如果问题看起来很迟钝或涉及太多不同的主题,我们深表歉意。
编辑:为避免疑义,我 不是 寻找建议我只是通过引用第一个字段手动创建新案例 class 的答案, 如;
val freddieB = B(fieldARenamed = freddie.fieldA, fieldB = freddie.fieldB, fieldC = freddie.fieldC, fieldCTransformed =freddie.fieldC.toUpperCase)
请参阅下面的评论,了解为什么这是不合适的。
最简单的解决方案是使用旧实例的值构造新实例 case class
,并根据需要对这些值应用函数。代码将非常高效,代码的目的将非常明确,编写它比任何其他解决方案花费的时间都更少,它比依赖于 third-party 库的解决方案更健壮和可维护,并且它避免了两者之间隐藏的依赖关系 类.
另一种选择是使用 automapper; in particular, Dynamic Mappings 功能。
对于您的特定示例,它看起来如下所示:
import io.bfil.automapper._
case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)
val freddie = new A(1,"Freddie","somestring")
val freddieB = automap(freddie).dynamicallyTo[B](
fieldARenamed = freddie.fieldA,
fieldCTransformed = freddie.fieldC.toUpperCase
)
我想你可以把它变成一个函数
def atob(a: A): B = {
automap(a).dynamicallyTo[B](
fieldARenamed = a.fieldA,
fieldCTransformed = a.fieldC.toUpperCase
)
}
从效率的角度来看,这个库使用了宏,所以generated code几乎和手写的一样好
仅供参考,这是让您的问题代码起作用的一种方法。
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{Align,Intersection}
import shapeless.syntax.singleton._
case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)
val fromGen = LabelledGeneric[A]
val toGen = LabelledGeneric[B]
val freddie = A(1, "Freddie", "somestring")
val putARename = Symbol("fieldARenamed") ->> freddie.fieldA
val putCTrans = Symbol("fieldCTransformed") ->> freddie.fieldC.toUpperCase
trait Field { type K; type V; type F = FieldType[K, V] }
object Field {
def apply[K0,V0](sample: FieldType[K0,V0]) =
new Field { type K = K0; type V = V0 }
}
val pFieldA = Field(putARename)
val pFieldCT = Field(putCTrans)
val inter = Intersection[pFieldA.F :: pFieldCT.F :: fromGen.Repr, toGen.Repr]
val align = Align[inter.Out, toGen.Repr]
toGen.from(align(inter(putARename :: putCTrans :: fromGen.to(freddie))))
//res0: B = B(1,Freddie,somestring,SOMESTRING)