如何使用Symbol或type对象将泛型类型传递到泛型类型函数中



在Scala中,是否可以将从Symbol或type对象派生的类型传递到泛型类型函数中?例如:

case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
def a[T: TypeTag](): Unit = {
val fields: Seq[Symbol] = typeOf[T].members.filter(_.isMethod == false).toSeq
fields.foreach(x => {
b[x.getMyType]() // How to pass field's "Type" into generic typed function?
})
}
def b[T](): Unit = ???
a[Person]()

从上面的例子中,我对调用a[Person]()很感兴趣,在a((中,使用反射从Person获得字段,以便使用每个字段的类型调用b[?]()

是否可以将从Symbol或type对象派生的类型传递到泛型类型函数中?

方法b的类型参数T必须在编译时已知,但x.typeSignature只有在运行时才已知。

您可以尝试使用编译时反射,而不是运行时反射。然后,x.typeSignature在宏的运行时(即主代码的编译时(变为已知。

// macros subproject
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def a[T](): Unit = macro aImpl[T]
def aImpl[T: c.WeakTypeTag](c: blackbox.Context)(): c.Tree = {
import c.universe._
val fields: Seq[Symbol] = weakTypeOf[T].members.filter(_.isMethod == false).toSeq
val bCalls = fields.map(x => 
q"b[${x.typeSignature}]()"
)
q"..$bCalls"
}
// main subproject
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
def b[T](): Unit = ???
a[Person]()
// scalac: {
//  b[App.Address]();
//  b[Int]();
//  b[String]()
//}

类似的事情也可以用Shapeless来完成。

import shapeless.ops.hlist.{FillWith, Mapper}
import shapeless.{Generic, HList, Poly0, Poly1}
def b[T](): Unit = println("b")
object bPoly extends Poly1 {
implicit def cse[X]: Case.Aux[X, Unit] = at(_ => b[X]())
}
object nullPoly extends Poly0 {
implicit def cse[X]: Case0[X] = at(null.asInstanceOf[X])
}
def a[T] = new PartiallyAppliedA[T]
class PartiallyAppliedA[T] {
def apply[L <: HList]()(implicit
generic: Generic.Aux[T, L],
mapper: Mapper[bPoly.type, L],
fillWith: FillWith[nullPoly.type, L]
): Unit = mapper(fillWith())
}
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
a[Person]()
//b
//b
//b

或者,如果您真的想使用运行时反射,则必须将b[...]()的编译推迟到运行时。你可以用工具箱来做这件事。

import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()
def a[T: TypeTag](): Unit = {
val fields: Seq[Symbol] = typeOf[T].members.filter(_.isMethod == false).toSeq
val bCalls = fields.map(x => 
q"b[${x.typeSignature}]()"
)
toolbox.eval(q"""
import Obj._
..$bCalls
""")
}
object Obj {
def b[T](): Unit = println("b")
}
case class Address(street: String, city: String, state: String, zipCode: String)
case class Person(name: String, age: Int, address: Address)
a[Person]()
//b
//b
//b

最新更新