Chr*_*ris 5 scala metaprogramming type-erasure
考虑一个通用函数:
def genericFn[T](fn: T => Boolean): Unit = {
// do something involves T
}
Run Code Online (Sandbox Code Playgroud)
是否可以限制T(在编译时)一个简单的类型,而不是类似的类型List[Int]?
我想要解决的基础问题是这样的:
var actorReceive: Receive = PartialFunction.empty
def addCase[T](handler: T => Boolean): Unit = {
actorReceive = actorReceive orElse ({
case msg: T => // call handle at some point, plus some other logic
handler(msg)
})
}
Run Code Online (Sandbox Code Playgroud)
该addCase函数将导致类型擦除警告,这可以通过要求ClassTag如下来解决:def addCase[T: ClassTag](...,但ClassTag仍然无法防止如下调用:
addCase[List[Int]](_ => {println("Int"); true})
addCase[List[String]](_ => {println("String"); false})
actorReceive(List("str")) // will print "Int"
Run Code Online (Sandbox Code Playgroud)
上面的代码会打印"Int"而不发出任何警告或错误,有什么办法吗?
在没有反射的情况下,没有办法在类型系统中强制执行此操作.
最好的方法是使用类型类,例如NonEraseable[A],提供类型没有在运行时擦除的类型参数的证据.隐含NonEraseable[A]的范围应该意味着A没有类型参数.看到手动创建这些将是繁琐的,隐式宏可以完成这项工作:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
trait NonEraseable[A]
object NonEraseable {
implicit def ev[A]: NonEraseable[A] = macro evImpl[A]
def evImpl[A](c: Context)(implicit tt: c.WeakTypeTag[A]): c.Expr[NonEraseable[A]] = {
import c.universe._
val tpe = weakTypeOf[A]
if(tpe.dealias.typeArgs.isEmpty)
c.Expr[NonEraseable[A]](q"new NonEraseable[$tpe] {}")
else
c.abort(c.enclosingPosition, s"$tpe contains parameters that will be erased at runtime.")
}
}
Run Code Online (Sandbox Code Playgroud)
使用案例:
def onlySimple[A : NonEraseable](value: A): Unit = println(value)
scala> onlySimple(1)
1
scala> onlySimple(List(1, 2, 3))
<console>:13: error: List[Int] contains parameters that will be erased at runtime.
onlySimple(List(1, 2, 3))
^
Run Code Online (Sandbox Code Playgroud)
使用此方法,您可以在编译时强制执行A具有上下文绑定的类型参数NonEraseable是您想要的类型.(假设你没有作弊并手动创建类型类的实例)