作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
假设有人提供了一个函数:
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. :(
}
}
更具体地说,在我看来,我在这里问两个问题:
最佳答案
您找不到任何相当简单或直接的方法来做到这一点。如果您准备好接受更复杂的解决方案,请耐心等待。
每个案例类在其伴生对象中都有一个 apply
方法,用于实例化该类。通过在此方法上调用 tupled
(在 eta 扩展之后),您将获得一个接受元组并创建相应案例类实例的函数。
现在的问题当然是每个案例类的 apply
都有不同的签名。我们可以通过引入一个代表案例类工厂的类型类来解决这个问题,并通过宏提供该类型类的实例(它只会委托(delegate)给案例类的 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]: CaseClassFactory[C,T] = macro factoryImpl[C,T]
def apply[C]: CaseClassFactory[C,_] = macro factoryImpl[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)
}
}
关于scala - 如何在 Scala 中进行泛型元组 -> 案例类转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30728759/
我是一名优秀的程序员,十分优秀!