在scala中,如何将一个case类转换为另一个不受代码更改和字段添加影响的case类?

在scala中,如何将一个case类转换为另一个不受代码更改和字段添加影响的case类?,scala,Scala,我想从Cat构造Kitten,但希望在不传递所有参数的情况下自动执行(因此,如果我向Cat/Kitten添加更多字段,我就不需要更改代码)。在scala中是否有一种紧凑的方法来实现这一点 case class Cat(color: Int, isFat: Boolean) case class Kitten(color: Int, isFat: Boolean) 假设Cat和Kitten具有相同的字段,您可以执行以下操作: val kitten = Cat(1,True).what_to_do

我想从Cat构造Kitten,但希望在不传递所有参数的情况下自动执行(因此,如果我向Cat/Kitten添加更多字段,我就不需要更改代码)。在scala中是否有一种紧凑的方法来实现这一点

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

假设
Cat
Kitten
具有相同的字段,您可以执行以下操作:
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.
scala>case类Cat(颜色:Int,isFat:Boolean) 定义类Cat scala>case类Kitten(颜色:Int,isFat:Boolean) 定义类小猫 scala>Cat(1,正确) res0:Cat=Cat(1,真) scala>Kitten.tuple(Cat.unapply(res0.get)//此行将保持不变 res1:Kitten=Kitten(1,真)

scala>case类Cat(颜色:Int,isFat:Boolean,isBrown:Boolean) 定义类Cat scala>case类Kitten(颜色:Int,isFat:Boolean,isBrown:Boolean) 定义类小猫 scala>Kitten.tuple(Cat.unapply(Cat(2,true,false)).get) res3:Kitten=Kitten(2,对,错)


显然,如果只是任意更改一个case类,那么这将不起作用

如果两个类的参数相同,应该可以工作。如果更改了一个,您将得到编译器错误

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)
你可能想看看

Shapess是一个用于泛型编程的库。例如,泛型typeclass可以将密封特征和案例类的任何层次结构的实例(例如
Cat
Kitten
)转换为泛型表示(HLists和Coproducts),然后再转换回任何兼容的类层次结构。中间的泛型表示可以以类型安全的方式进行操作

genKit.to(kitten)
获取一只
kitten
,并生成一个HList
2::true::HNil
。由于它与
Cat
的通用表示法兼容,无需修改,因此可以使用
genCat.from
存储它

在这种情况下,
Cat
Kitten
几乎相同。如果类型的顺序不同,或者小猫有额外的属性,该怎么办?如果每个属性的名称都很重要怎么办?在Shapeless中有很多有用的东西,可以通过操纵泛型表示轻松准确地解决这类情况。请看一个示例,其中使用LabelledGeneric(使用带有标签的HLists,也称为记录)转换某些类型的Book,并添加一个属性,并将其存储到ExtendedBook中。所有这类产品都是安全的


Shapeless确实使用了一些宏,但似乎只依赖于它们的一小部分。Shapeless的用户自己并不编写任何宏—这项繁重的工作是由Scala强大的类型系统完成的。

我为这种转换创建了带有宏的小型库

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

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)

如果可能,它会尝试进行隐式转换,使用命名参数构造实例,这样顺序就不重要了,结果对象类的字段数可以比原始值少,并且原始值中不存在的字段的结果类型构造函数中的默认值也应该可以工作。

您需要一个宏来实现这一点。我可能会将字段放入第三个类中,该类包括
Cat
Kitten
have as-field。你怎么知道额外字段的值必须是多少?@ChrisMartin这是一个例子,有时我需要解耦系统,但仍然希望具有从一个DTO到另一个DTO转换的灵活性。@Jas如果你更改一个类而不更改另一个,转换将无法继续工作,那么,这些类“解耦”到底意味着什么呢?
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)
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)