[英]Why doesn't my validation throw exception when it checks for the input type?
我的方法:
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值。 为什么不呢?
我的目标是验证已将有效负载发送给参与者,参与者仅应对特定类型的资源做出反应,而别无其他。 我该如何解决这个问题?
最简单的情况是可以使用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
。 创建有效负载时, 必须知道资源的类型,并且有效负载必须存储其Type
或该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
创建Payload
之前resource
类型为Any
,则仅当T
为Any
validatePayload[T]
不会抛出。 还有其他一些怪癖:
// 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)))))
第三方库shapeless
提供了更强大的方法。 这是一个例子:
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.")
}
}
}
}
两者都不抛出:
validatePayload[Seq[Int]](Some(Payload("id", Some(List(1,2,3)))))
validatePayload[List[Int]](Some(Payload("id", Some(Seq(1,2,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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.