我想模式匹配一个函数,问题是类型擦除.请注意,在下面的代码段中,尽管发出警告,但发生了匹配并且发生了“错误”.
scala> def f1 = ()=>true f1: () => Boolean scala> val fl = f1 fl: () => Boolean = <function0> scala> scala> fl match { | case fp :Function0[Boolean] => 1 | case _ => 2 | } res8: Int = 1 scala> scala> fl match { | case fp :Function0[String] => 1 | case _ => 2 | } <console>:11: warning: fruitless type test: a value of type () => Boolean cannot also be a () => String (but still might match its erasure) case fp :Function0[String] => 1 ^ res9: Int = 1 scala>
我能想出的是一个包含该功能的案例类.我得到了类型安全,请注意下面的错误.但是,这首先是不优雅的,其次,我不明白案例类如何强制执行类型,而模式匹配却不能.我唯一的猜测是case类受编译器保护,并且只在运行时解析匹配
scala> case class FunctionWrapper(fn: ()=>Boolean) defined class FunctionWrapper scala> val fw = FunctionWrapper(fl) fw: FunctionWrapper = FunctionWrapper(<function0>) scala> def fs = ()=>"whatever" fs: () => String scala> val fws = FunctionWrapper(fs) <console>:10: error: type mismatch; found : () => String required: () => Boolean val fws = FunctionWrapper(fs) ^ scala> fw match { | case FunctionWrapper(f) => f() | case _ => false | } rES10: Boolean = true
解决方法
简短的回答:你必须撤消擦除,使用TypeTag来修改类型.
I don’t understand how the case class can enforce types whereas the pattern match can’t.
因为您的case类没有类型参数.只删除泛型类型,这就是它被称为“部分擦除”的原因.
相关问题:Generic unapply method for different types of List.以下代码与其中一个答案基本相同,但使用函数而不是列表:
import scala.reflect.runtime.universe._ def foo[A : TypeTag](a: A): Int = typeOf[A] match { case t if t =:= typeOf[Int => Int] => a.asInstanceOf[Int => Int](0) case t if t =:= typeOf[Boolean => Int] => a.asInstanceOf[Boolean => Int](true) case _ => 3 } foo((i: Int) => i + 1) // res0: Int = 1 foo((b: Boolean) => if (b) 2 else 0) // res1: Int = 2 foo((b: Boolean) => !b) // res2: Int = 3
如果您需要以丢失静态类型信息的方式传递这些函数(将它们推送到函数[_,_]的集合中,然后使用Akka消息等),那么您还需要传递标记:
import scala.reflect.runtime.universe._ case class Tagged[A](a: A)(implicit val tag: TypeTag[A]) def foo[A,B](tagged: Tagged[A => B]): Int = tagged.tag.tpe match { case t if t =:= typeOf[Int => Int] => tagged.a.asInstanceOf[Int => Int](0) case t if t =:= typeOf[Boolean => Int] => tagged.a.asInstanceOf[Boolean => Int](true) case _ => 3 } foo(Tagged((i: Int) => i + 1)) // res0: Int = 1 foo(Tagged((b: Boolean) => if (b) 2 else 0)) // res1: Int = 2 foo(Tagged((b: Boolean) => !b)) // res2: Int = 3