我的方法:
protected final def validatePayload[T](payload: Option[Payload]) = {
payload match {
case None => throw new IllegalArgumentException("Payload was None.")
case Some(p) => p.resource match {
case None => throw new IllegalArgumentException("Resource was None.")
case Some(resource) => resource match {
case temp: T =>
case _ => throw new IllegalArgumentException("Resource is not the right type.")
}
}
}
}
有效载荷:
case class Payload(id: String, resource: Option[Any])
用法:
validatePayload[String](Some(Payload("id", Some(5))))
我希望这会抛出非法的arg,因为我告诉它接受String并且我传入了Int。为什么不呢?
我的目标是验证已发送给actor的有效负载,actor应该只对特定类型的资源做出反应,而不是其他任何事情。 我该如何解决这个问题呢?
答案 0 :(得分:4)
最简单的情况是你可以使用ClassTag
(下面给出了这种情况的限制)。对于这种情况,您可以简单地添加绑定到函数类型定义的上下文,它只是起作用:
import scala.reflect.ClassTag
protected final def validatePayload[T : ClassTag](payload: Option[Payload]) = {
// everything else is the same...
}
// Throws an error
validatePayload[String](Some(Payload("id", Some(5))))
在运行时,它几乎等同于Java的instanceof
运算符和类型转换。
但ClassTag
不适用于泛型类型。例如,不区分具有不同元素类型的序列:
// Doesn't throw
validatePayload[Seq[String]](Some(Payload("id", Some(Seq(1,2,3)))))
如果您需要区分通用类型,则必须使用TypeTag
s。在创建有效负载时,必须知道资源的类型,并且有效负载必须存储其类型的Type
或TypeTag
。
以下是一个例子:
import reflect.runtime.universe._
case class Payload[T](id: String, resource: Option[T])(implicit val tag: TypeTag[T])
def validatePayload[T : TypeTag](payload: Option[Payload[_]]) = {
payload match {
case None => throw new IllegalArgumentException("Payload was None.")
case Some(p) => p.resource match {
case None => throw new IllegalArgumentException("Resource was None.")
case Some(resource) => resource match {
case temp if p.tag.tpe <:< typeOf[T] =>
case _ => throw new IllegalArgumentException("Resource is not the right type.")
}
}
}
}
现在它将区分泛型:
// Throws an error
validatePayload[Seq[String]](Some(Payload("id", Some(Seq(1,2,3)))))
但是TypeTag
依赖于编译时已知的类型。因此,如果resource
在使用Any
创建Payload
之前已validatePayload[T]
,那么T
仅在Any
为// Doesn't throw
validatePayload[Seq[Int]](Some(Payload("id", Some(List(1,2,3)))))
// Throws, although resource *is* a List[Int] at runtime
validatePayload[List[Int]](Some(Payload("id", Some(Seq(1,2,3)))))
时才会抛出。还有一些其他的怪癖:
import shapeless.Typeable
import shapeless.syntax.typeable._
def validatePayload[T : Typeable](payload: Option[Payload]) = {
payload match {
case None => throw new IllegalArgumentException("Payload was None.")
case Some(p) => p.resource match {
case None => throw new IllegalArgumentException("Resource was None.")
case Some(resource) => resource match {
case temp if temp.cast[T].isDefined =>
case _ => throw new IllegalArgumentException("Resource is not the right type.")
}
}
}
}
第三方库shapeless
提供了更强大的方法。这是一个例子:
validatePayload[Seq[Int]](Some(Payload("id", Some(List(1,2,3)))))
validatePayload[List[Int]](Some(Payload("id", Some(Seq(1,2,3)))))
现在都不要扔:
dplyr
答案 1 :(得分:3)
由于类型擦除,您无法检查此类型,但ClassTag
是一种解决方法。
case class Payload(id: String, resource: Option[Any])
import scala.reflect.ClassTag
def validatePayload[T: ClassTag](payload: Option[Payload]) = {
payload flatMap (_.resource) filter { res =>
val c = implicitly[ClassTag[T]].runtimeClass
c.isInstance(res)
} getOrElse (throw new IllegalArgumentException("Invalid payload"))
}
我简化了代码,如果您不需要自定义错误,至少对我来说不那么冗长。虽然如果你想坚持你的代码,只有问题视图中的重要部分声明类型T
需要和隐式ClassTag[T]
声明为[T: ClassTag]
,并检查类型在这里有效:
val c = implicitly[ClassTag[T]].runtimeClass
c.isInstance(res)
这是一个测试
scala> validatePayload[String](Some(Payload("id", Some("a"))))
res3: Any = a
scala> validatePayload[String](Some(Payload("id", Some(5))))
java.lang.IllegalArgumentException: Invalid payload
at $anonfun$validatePayload$3.apply(<console>:20)
at $anonfun$validatePayload$3.apply(<console>:20)
at scala.Option.getOrElse(Option.scala:121)
at .validatePayload(<console>:20)
... 33 elided