Scala 当参数列表相同时,将一个case类转换为另一个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,
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
的函数,但也没有成功。