使用存储为通配符 classTag 的类型调用模板 Scala 函数?
Invoke a template Scala function with a type stored as wild card classTag?
我定义了以下 class 来存储对象的 class 标签:
case class BindObj (bindTypeA: ClassTag[_], bindTypeB: ClassTag[_]) {
}
和如下模板函数:
def workOnThis[A, B](left: A, right: B): Unit => {
someOtherFunction(A, B)
...
}
预期用途如下:
def doWork(left: Anyref, right: Anyref, bindObj: BindObj): Unit => {
workOnThis[BindObj.bindTypeA, BindObj.bindTypeB](left.asInstance[BindObj.bindTypeA], right[BindObj.bindTypeB])
}
这段代码显然行不通,因为模板类型需要在编译时知道。
但是是否有解决方法来实现预期用途?
你的问题听起来像 XY problem。您应该提供有关您实际想要做什么的更多详细信息。标准的 Scala 代码可能就足够了(也许有转换),或者宏就足够了(如果事情可以在编译时完成),或者运行时反射就足够了(至少没有反射编译)。
我假设您实际上仅在运行时拥有有关类型 A, B
(ClassTag[A]
、ClassTag[B]
)的信息,并且方法 workOnThis
实际上表现不同,具体取决于 A
, B
.
看起来很奇怪,但原则上你可以这样做:
import scala.reflect.api.TypeCreator
import scala.reflect.{ClassTag, api, classTag}
import scala.reflect.runtime.universe.{Quasiquote, Type, TypeTag, weakTypeOf}
import scala.reflect.runtime.{currentMirror => cm}
import scala.tools.reflect.ToolBox
object App {
val tb = cm.mkToolBox()
// from (1)
def backward[T](tpe: Type): TypeTag[T] =
TypeTag(cm, new TypeCreator {
override def apply[U <: api.Universe with Singleton](m: api.Mirror[U]): U#Type =
if (m eq cm) tpe.asInstanceOf[U#Type]
else throw new IllegalArgumentException(s"Type tag defined in $cm cannot be migrated to other mirrors.")
})
def classTagToTypeTag[T](classTag: ClassTag[T]): TypeTag[T] = {
val symbol = cm.classSymbol(classTag.runtimeClass)
symbol.typeParams // side effect
backward(symbol.toType)
}
case class BindObj(bindTypeA: ClassTag[_], bindTypeB: ClassTag[_])
def workOnThis[A, B](left: A, right: B): Unit =
println(s"A=${weakTypeOf[A]}, B=${weakTypeOf[B]}, left=$left, right=$right")
def doWork(left: AnyRef, right: AnyRef, bindObj: BindObj): Unit =
tb.eval(q"""
App.workOnThis[
${classTagToTypeTag(bindObj.bindTypeA)},
${classTagToTypeTag(bindObj.bindTypeB)}
](_, _)
""")
.asInstanceOf[(AnyRef, AnyRef) => Unit]
.apply(left, right)
class A
class B
def main(args: Array[String]): Unit = {
doWork(new A, new B, BindObj(classTag[A], classTag[B]))
// A=A, B=B, left=App$A@75e09567, right=App$B@2a334bac
}
}
(1)
我定义了以下 class 来存储对象的 class 标签:
case class BindObj (bindTypeA: ClassTag[_], bindTypeB: ClassTag[_]) {
}
和如下模板函数:
def workOnThis[A, B](left: A, right: B): Unit => {
someOtherFunction(A, B)
...
}
预期用途如下:
def doWork(left: Anyref, right: Anyref, bindObj: BindObj): Unit => {
workOnThis[BindObj.bindTypeA, BindObj.bindTypeB](left.asInstance[BindObj.bindTypeA], right[BindObj.bindTypeB])
}
这段代码显然行不通,因为模板类型需要在编译时知道。 但是是否有解决方法来实现预期用途?
你的问题听起来像 XY problem。您应该提供有关您实际想要做什么的更多详细信息。标准的 Scala 代码可能就足够了(也许有转换),或者宏就足够了(如果事情可以在编译时完成),或者运行时反射就足够了(至少没有反射编译)。
我假设您实际上仅在运行时拥有有关类型 A, B
(ClassTag[A]
、ClassTag[B]
)的信息,并且方法 workOnThis
实际上表现不同,具体取决于 A
, B
.
看起来很奇怪,但原则上你可以这样做:
import scala.reflect.api.TypeCreator
import scala.reflect.{ClassTag, api, classTag}
import scala.reflect.runtime.universe.{Quasiquote, Type, TypeTag, weakTypeOf}
import scala.reflect.runtime.{currentMirror => cm}
import scala.tools.reflect.ToolBox
object App {
val tb = cm.mkToolBox()
// from (1)
def backward[T](tpe: Type): TypeTag[T] =
TypeTag(cm, new TypeCreator {
override def apply[U <: api.Universe with Singleton](m: api.Mirror[U]): U#Type =
if (m eq cm) tpe.asInstanceOf[U#Type]
else throw new IllegalArgumentException(s"Type tag defined in $cm cannot be migrated to other mirrors.")
})
def classTagToTypeTag[T](classTag: ClassTag[T]): TypeTag[T] = {
val symbol = cm.classSymbol(classTag.runtimeClass)
symbol.typeParams // side effect
backward(symbol.toType)
}
case class BindObj(bindTypeA: ClassTag[_], bindTypeB: ClassTag[_])
def workOnThis[A, B](left: A, right: B): Unit =
println(s"A=${weakTypeOf[A]}, B=${weakTypeOf[B]}, left=$left, right=$right")
def doWork(left: AnyRef, right: AnyRef, bindObj: BindObj): Unit =
tb.eval(q"""
App.workOnThis[
${classTagToTypeTag(bindObj.bindTypeA)},
${classTagToTypeTag(bindObj.bindTypeB)}
](_, _)
""")
.asInstanceOf[(AnyRef, AnyRef) => Unit]
.apply(left, right)
class A
class B
def main(args: Array[String]): Unit = {
doWork(new A, new B, BindObj(classTag[A], classTag[B]))
// A=A, B=B, left=App$A@75e09567, right=App$B@2a334bac
}
}
(1)