【问题标题】:How to pass into generic type using a Symbol or Type object into a generic typed function?如何使用 Symbol 或 Type 对象将泛型类型传递给泛型类型函数?
【发布时间】:2020-12-03 21:28:17
【问题描述】:

在 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[?]()。

【问题讨论】:

  • 这看起来像一个 XY 问题,你到底为什么需要这个?运行时反射是不安全、缓慢、不安全和不可移植的。此外,实际上泛型在运行时并不存在,除了类之外没有类型。
  • @LuisMiguelMejíaSuárez 你认为所有反思问题都是XY吗? :)
  • @DmytroMitin 简短回答是 :) - 长回答。鉴于此:首先,我认为 (runtime) reflection 是最后的手段。其次,我真的没有知识来回答这个问题。第三,我知道其他人(通常是你)会为特定问题提供完整的答案(我总是赞成);然后,如果他/她真的确定反思确实是他/她的问题所需要的,或者是否有其他方法,我更愿意发表评论告诉 OP。

标签: java scala generics reflection scala-reflect


【解决方案1】:

是否可以将派生自 Symbol 或 Type 对象的类型传递给泛型类型函数?

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

您可以尝试使用compile-time reflection 而不是运行时之一。然后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[...]() 的编译推迟到运行时。您可以使用toolbox 来执行此操作。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-19
    • 2019-03-05
    • 1970-01-01
    相关资源
    最近更新 更多