如何在Scala中执行通用元组 – >案例类转换?
发布时间:2020-12-16 09:55:14 所属栏目:安全 来源:网络整理
导读:假设有人提供了一个功能: def getTupleData[T](source: String): List[T] = { // ...} 我需要编写一个函数,它将一个case类C作为类型参数,并在上述函数的帮助下返回List [C].这是我到目前为止所得到的: def getCaseClassData[C](source: String): List[C] =
假设有人提供了一个功能:
def getTupleData[T](source: String): List[T] = { // ... } 我需要编写一个函数,它将一个case类C作为类型参数,并在上述函数的帮助下返回List [C].这是我到目前为止所得到的: def getCaseClassData[C](source: String): List[C] = { // Somehow get T from C. // For example,if C is case class MyCaseClass(a: Int,b: Long),then T is (Int,Long) // How to get T? getTupleData[T](source) map { tuple: T => // Somehow convert tuple into a case class instance with the case class type parameter // C.tupled(tuple) ?? Type parameter cannot be used like this. :( } } 更具体地说,在我看来,我在这里问两个问题: >如何从表示case类的类型参数显式获取元组的类型,以便它可以用作类型参数? 解决方法
您将找不到任何合理简单或直接的方法.如果你准备好参与更多的解决方案,请耐心等待.
每个case类在其伴随对象中都有一个apply方法,该方法实例化该类.通过在这个方法上调用tupled(在eta-expansion之后),你将得到一个带有元组的函数并创建相应的case类实例. 当然,问题是每个案例类的应用都有不同的签名.我们可以通过引入表示case类工厂的类型类来解决这个问题,并通过宏(它将委托给case类的apply方法)提供这个类型类的实例. import scala.reflect.macros.whitebox.Context import scala.language.experimental.macros trait CaseClassFactory[C,T]{ type Class = C type Tuple = T def apply(t: Tuple): C } object CaseClassFactory { implicit def factory1[C,T]: CaseClassFactory[C,T] = macro factoryImpl[C,T] implicit def factory2[C]: CaseClassFactory[C,_] = macro factoryImpl[C,Nothing] def apply[C,T] def apply[C]: CaseClassFactory[C,Nothing] def factoryImpl[C:c.WeakTypeTag,T:c.WeakTypeTag](c: Context) = { import c.universe._ val C = weakTypeOf[C] val companion = C.typeSymbol.companion match { case NoSymbol => c.abort(c.enclosingPosition,s"Instance of $C has no companion object") case sym => sym } val tupledTree = c.typecheck(q"""($companion.apply _).tupled""") val T = tupledTree.tpe match { case TypeRef(_,_,List(argTpe,_)) => argTpe case t => c.abort(c.enclosingPosition,s"Expecting type constructor (Function1) for $C.tupled,but got $t: ${t.getClass},${t.getClass.getInterfaces.mkString(",")}") } if (! (c.weakTypeOf[T] <:< T)) { c.abort(c.enclosingPosition,s"Incompatible tuple type ${c.weakTypeOf[T]}: not a sub type of $T") } q""" new CaseClassFactory[$C,$T] { private[this] val tupled = ($companion.apply _).tupled def apply(t: Tuple): $C = tupled(t) } """ } } 有了它你可以做这样的事情: scala> case class Person(name: String,age: Long) defined class Person scala> val f = CaseClassFactory[Person] f: CaseClassFactory[Person]{type Tuple = (String,Long)} = $anon$1@63adb42c scala> val x: f.Tuple = ("aze",123) x: f.Tuple = (aze,123) scala> implicitly[f.Tuple =:= (String,Long)] res3: =:=[f.Tuple,(String,Long)] = <function1> scala> f(("aze",123)) res4: Person = Person(aze,123) 但更重要的是,您可以要求CaseClassFactory的实例作为隐式参数,允许通常实例化您的案例类.然后你可以这样做: scala> implicit class TupleToCaseClassOps[T](val t: T) extends AnyVal { | def toCaseClass[C](implicit f: CaseClassFactory[C,T]): C = { | f(t) | } | } defined class TupleToCaseClassOps scala> case class Person(name: String,age: Long) defined class Person scala> ("john",21).toCaseClass[Person] res5: Person = Person(john,21) 很简约.使用此类型类,getCaseClassData将变为: def getCaseClassData[C](source: String)(implicit f: CaseClassFactory[C,_]): List[C] = { getTupleData[f.Tuple](source) map { tuple: f.Tuple => f(tuple) } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |