问题是:
如何构建一个通用函数,该函数可以采用由其他案例类组成的任何案例类,然后将其平整为一个案例类,并将所有案例类中的所有值都组合到一个案例类中? < / p>
例如,我想像这样转换嵌套案例类:
case class A(first: String, second: String)
case class B(value: String)
case class Nested(a: A, b: B)
像这样的扁平案例类:
case class Flatten(aFirst: String, aSecond: String, bValue: String)
但是我想避免构建自己的构造函数(或手动创建函数),如下所示:
object Flatten {
def apply(nested: Nested): Flatten = {
Flatten(nested.a.first, nested.a.second, nested.b.value)
}
}
注意:在实际用例中,用例类更为复杂,我想在不同的用例类上多次使用该方法。
答案 0 :(得分:2)
假设目标案例类字段名称具有预定义格式,则可以使用反射api。看看例子
import scala.reflect.runtime.universe._
class Converter(any: Any) {
private val rm = runtimeMirror(any.getClass.getClassLoader)
private def nameToPath(name: String, pathElem: String = "", pathElems: List[String] = List()): List[String] =
if (name.isEmpty) pathElems :+ pathElem.toLowerCase()
else if (name.head.isUpper) nameToPath(name.tail, name.head.toString, pathElems :+ pathElem)
else nameToPath(name.tail, pathElem + name.head, pathElems)
private def valueByPath(v: Any, pathElems: List[String]): Any =
if (pathElems.isEmpty) v
else {
val im = rm.reflect(v)
val fieldName = TermName(pathElems.head)
val field = im.symbol.info.member(fieldName).asTerm
val value = im.reflectField(field).get
valueByPath(value, pathElems.tail)
}
def convertTo[T: TypeTag]: T = {
val target = typeOf[T]
val fieldNames = target.decls.sorted.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}
val paths = fieldNames.map(s => nameToPath(s.name.toString))
val values = paths.map(valueByPath(any, _))
val constructorSymbol = target.decl(termNames.CONSTRUCTOR)
val defaultConstructor = constructorSymbol match {
case cs: MethodSymbol => cs
case ts: TermSymbol =>
ts.alternatives.collectFirst {
case ms: MethodSymbol if ms.isPrimaryConstructor => ms
}.get
}
val cs = target.typeSymbol.asClass
val cm = rm.reflectClass(cs)
val constructor = cm.reflectConstructor(defaultConstructor)
constructor(values: _*).asInstanceOf[T]
}
}
implicit class AnyOps(any: Any) {
def to[T: TypeTag]: T = new Converter(any).convertTo[T]
}
使用
val a = A("1", "2")
val b = B("3")
val n = Nested(a, b)
val r = n.to[Flatten]
输出
r: Flatten = Flatten(1,2,3)