Scala 当参数列表相同时,将一个case类转换为另一个case类

Scala 当参数列表相同时,将一个case类转换为另一个case类,scala,boilerplate,Scala,Boilerplate,我有很多相似的case类,它们的意思不同,但是有相同的参数列表 object User { case class Create(userName:String, firstName: String, lastName: String) case class Created(userName:String, firstName: String, lastName: String) } object Group { case class Create(groupName:String,

我有很多相似的case类,它们的意思不同,但是有相同的参数列表

object User {
  case class Create(userName:String, firstName: String, lastName: String)
  case class Created(userName:String, firstName: String, lastName: String)
}

object Group {
  case class Create(groupName:String, members: Int)
  case class Created(groupName:String, members: Int)
}
考虑到这种设置,我已经厌倦了编写接受Create类型的参数并返回Created类型的参数的方法。我有大量的测试用例可以做这种事情

我可以编写一个函数将一个case类转换为另一个case类。此函数将
User.Create
转换为
User.Created

def userCreated(create: User.Create) = User.Create.unapply(create).map((User.Created.apply _).tupled).getOrElse(sys.error(s"User creation failed: $create"))
我不得不为小组编写另一个这样的函数。 我真正想要的是一个泛型函数,它接受两种类型的case类和一个case类的对象,并将其转换为另一个case类。大概

def transform[A,B](a: A):B

此外,此函数不应违背简化样板文件的目的。如果该功能更易于使用,请随意为其建议不同的签名。

为了拯救,请不要变形

您可以使用Shapeless的
Generic
创建案例类的泛型表示,然后可以使用它来完成您试图完成的任务。使用
LabelledGeneric
我们可以强制使用类型和参数名称

import shapeless._

case class Create(userName: String, firstName: String, lastName: String)
case class Created(userName: String, firstName: String, lastName: String)
case class SortOfCreated(screenName: String, firstName: String, lastName: String)

val c = Create("username", "firstname", "lastname")

val createGen = LabelledGeneric[Create]
val createdGen = LabelledGeneric[Created]
val sortOfCreatedGen = LabelledGeneric[SortOfCreated]

val created: Created = createdGen.from(createGen.to(c))

sortOfCreatedGen.from(createGen.to(c)) // fails to compile

请注意,下面是我设法实现的最简单的类型安全语法:

implicit class Convert[A, RA](value: A)(implicit ga: Generic.Aux[A, RA]) {
  def convertTo[B, RB](gb: Generic.Aux[B, RB])(implicit ev: RA =:= RB) =
    gb.from(ga.to(value))
}
它可以这样使用:

case class Create(userName: String, firstName: String, lastName: String)
case class Created(userName: String, firstName: String, lastName: String)

val created = Create("foo", "bar", "baz").convertTo(Generic[Created])
或者使用
LabelledGeneric
实现更好的类型安全性:

implicit class Convert[A, RA](value: A)(implicit ga: LabelledGeneric.Aux[A, RA]) {
  def convertTo[B, RB](gb: LabelledGeneric.Aux[B, RB])(implicit ev: RA =:= RB) =
    gb.from(ga.to(value))
}

val created = Create("foo", "bar", "baz").convertTo(LabelledGeneric[Created]))

您是否希望这样的转换(a).到[B]?所有这些成对的类都有创建/创建的名称是重要的,还是您的要求只是它们可以配对,并且一个函数将任何此类对的第一个类转换为第二个类?很好的解决方案!但是,有一个可能的陷阱:由于
Generic
的工作原理是将对象转换为
HList
,因此无法确保源对象和目标对象的相应参数名称匹配。所以,若您有相同类型或不同参数顺序的不同参数,则在运行时会得到错误的转换结果。有一种通过反射执行映射的方法可以解决这个问题。@Aivean这是一个实点。取决于您是否关心参数名称或类型。这是一种值类为离合器的情况。@a这可以通过使用
LabelledGeneric
直接修复,这将需要案例类参数名称和类型及其顺序的精确对应。我想知道是否可以定义一个类似于OP:
def transform[a:generic,B:generic](a:a):B
中的泛型函数。我找不到一个隐式参数来向编译器证明case类模式相等<代码>隐式ev:Generic[A]#Repr=:=Generic[B]#Repr编译失败。@Kolmar我试图定义一个类似于
transform
的函数,但也没有成功。