有没有一种优雅的方式可以从以下位置到达:
def foo[T: TypeTag](a: A[T]) {
// can we match on the type of T here?
}
如何针对T的类型进行匹配表达式?
显然,下面的代码并没有克服擦除的问题,因此我们必须手动检查TypeTag吗?
a match {
case _:A[SomeSpecificType] => ...
或者说Scala能为此提供一些优雅的解决方案吗?
有没有一种优雅的方式可以从以下位置到达:
def foo[T: TypeTag](a: A[T]) {
// can we match on the type of T here?
}
如何针对T的类型进行匹配表达式?
显然,下面的代码并没有克服擦除的问题,因此我们必须手动检查TypeTag吗?
a match {
case _:A[SomeSpecificType] => ...
或者说Scala能为此提供一些优雅的解决方案吗?
很遗憾,如果您在模式中添加类型检查,编译器不会考虑类型标签。我不确定为什么以及是否计划解决此问题。但是,您可以比较类型标签是否相等:
typeOf[T] =:= typeOf[List[String]]
import scala.reflect.runtime.universe._
class TypeTest[A: TypeTag]() {
def unapply[B: TypeTag](v: B): Option[A] =
if(typeOf[B] <:< typeOf[A])
Some(v.asInstanceOf[A])
else
None
}
object TypeTest {
def apply[A: TypeTag] = new TypeTest()
}
def printIfStrings[T: TypeTag](v: T) {
val string = TypeTest[List[String]]
v match {
case string(s) => printString(s)
case _ =>
}
}
def printString(s: List[String]) {
println(s)
}
printIfStrings(List(123))
printIfStrings(List("asd"))
val字符串
。@unchecked
来抑制这些警告。import scala.language.experimental.macros
import scala.language.implicitConversions
import scala.reflect.macros.blackbox.Context
object Switch {
implicit class Conversion[A](val value: A) {
def switch[B](f: PartialFunction[A, B]): B = macro switchImpl
}
def switchImpl(c: Context)(f: c.Tree): c.Tree = {
import c.universe._
val types = collection.mutable.Map[Tree,String]()
val t1 = new Transformer {
override def transformCaseDefs(trees: List[CaseDef]) = {
val t2 = new Transformer {
override def transform(tree: Tree) = {
def pattern(v: String, t: Tree) = {
val check = types.getOrElseUpdate(t, c.freshName())
pq"${TermName(check)}(${TermName(v)})"
}
tree match {
case Bind(TermName(v),Typed(Ident(termNames.WILDCARD),
Annotated(Apply(
Select(New(Ident(TypeName("unchecked"))),
termNames.CONSTRUCTOR), List()
), t)))
=> pattern(v,t)
case Bind(TermName(v),Typed(Ident(termNames.WILDCARD),t))
=> pattern(v,t)
case _ => super.transform(tree)
}
}
}
t2.transformCaseDefs(trees)
}
}
val tree = t1.transform(c.untypecheck(f))
val checks =
for ((t,n) <- types.toList) yield
q"val ${TermName(n)} = Switch.TypeTest[$t]"
q"""
..$checks
$tree(${c.prefix}.value)
"""
}
import scala.reflect.runtime.universe._
class TypeTest[A: TypeTag]() {
def unapply[B: TypeTag](v: B): Option[A] =
if(typeOf[B] <:< typeOf[A]) Some(v.asInstanceOf[A])
else None
}
object TypeTest {
def apply[A: TypeTag] = new TypeTest()
}
}
现在,模式中的魔法类型检查已经起作用:
import Switch.Conversion
val l = List("qwe")
def printIfStrings2[T: scala.reflect.runtime.universe.TypeTag](v: T) {
v switch {
case s: Int => println("int")
case s: List[String] @unchecked => printString(s)
case _ => println("none")
}
}
printIfStrings2(l)
printIfStrings2(List(1, 2, 3))
printIfStrings2(1)