在scala中如何将一个case类转换为另一个免费代码更改字段添加?

时间:2014-12-24 09:22:52

标签: scala

case class Cat(color: Int, isFat: Boolean)
case class Kitten(color: Int, isFat: Boolean)

我想从Cat构建Kitten但是想要自动完成而不传递所有参数(所以如果我向Cat / Kitten添加更多字段,我就不需要更改代码)。有没有一种紧凑的方法来实现scala?

val kitten = Cat(1,True).what_to_do_here? // compact & immune to code changes no need to change that line in case of added fields.

4 个答案:

答案 0 :(得分:13)

您可以查看Shapeless

import shapeless._
import shapeless.syntax._


case class Cat(color: Int, isFat: Boolean)
case class Kitten(color: Int, isFat: Boolean)

val kitten = Kitten(2, true)

val genCat = Generic[Cat]
val genKit = Generic[Kitten]

val cat: Cat = genCat.from(genKit.to(kitten))

println(cat)

Shapeless是泛型编程的库。例如,Generic类型类可以将密封特征和案例类(例如CatKitten)的任何层次结构的实例转换为通用表示形式(HLists和Coproducts),并返回到任何兼容的类层次结构。中间的通用表示可以以类型安全的方式进行操作。

genKit.to(kitten)获取Kitten,并生成HList 2 :: true :: HNil。由于它与Cat的通用表示形式兼容而不进行修改,因此可以使用genCat.from将其存储。

在这种情况下,CatKitten几乎相同。如果类型的顺序不同,或者Kitten有额外的属性怎么办?如果每个房产的名称都很重要怎么办? Shapeless中有大量有用的东西可以通过操纵通用表示来轻松解决这些情况。看一下this示例,其中使用LabelledGeneric(使用带有标签的HLists,a.k.a。记录)转换某些类型的Book,添加属性并存储到ExtendedBook中。所有这些都是安全的。

Shapeless 确实使用了一些宏,但看起来只依赖于它们的最小集合。 Shapeless的用户不会自己编写任何宏 - 繁重的工作由Scala强大的类型系统完成。

答案 1 :(得分:2)

假设CatKitten具有相同的字段,您可以这样做

scala> case class Cat(color: Int, isFat: Boolean)
defined class Cat
scala> case class Kitten(color: Int, isFat: Boolean)
defined class Kitten
scala>  Cat(1,true)
res0: Cat = Cat(1,true)
scala> Kitten.tupled(Cat.unapply(res0).get) // This line will remain same 
res1: Kitten = Kitten(1,true)

scala> case class Cat(color: Int, isFat: Boolean, isBrown: Boolean) defined class Cat scala> case class Kitten(color: Int, isFat: Boolean, isBrown: Boolean) defined class Kitten scala> Kitten.tupled(Cat.unapply(Cat(2,true, false)).get) res3: Kitten = Kitten(2,true,false)

显然,如果只有一个案例类被任意改变,这将不起作用。

答案 2 :(得分:1)

如果两个类具有相同的参数,则应该有效。如果更改了一个,则会出现编译错误。

case class Kitten(color: Int, isFat: Boolean)
case class Cat(color: Int, isFat: Boolean){
  def toKitten = Cat.unapply(this).map(Kitten.tupled).get
}

val kitten = Cat(1,true).toKitten

答案 3 :(得分:0)

我使用宏创建了一个小型库来进行此类转换。 https://github.com/anton-fomin/scala-convertible

它可以在编译时完成所有工作,您可以进行更复杂的转换,例如。

case class Foo(foo: String, bazz: FooBazz, seq: Seq[FooBazz], opt: Option[FooBazz], optNull: Option[FooBazz])

case class Bar(foo: Option[String], bazz: BarBazz, seq: Seq[BarBazz], opt: Option[BarBazz], optNull: BarBazz)

case class FooBazz(x: Int)

case class BarBazz(x: Int)

@UnwrapOptionModeOrNull
implicit def fooToBarBazz(bazz: FooBazz): BarBazz = convert[FooBazz, BarBazz](bazz)

@UnwrapOptionModeOrNull
def fooToBar(foo:Foo) = convert[Foo, Bar](foo)

它尝试在可能的情况下进行隐式转换,使用命名参数构造实例,因此顺序并不重要,结果对象类可以比结果类型构造函数中的原始和默认值少的字段用于不存在的字段原作也应该奏效。