[英]Why doesn't my validation throw exception when it checks for the input type?
My method: 我的方法:
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.")
}
}
}
}
Payload: 有效负载:
case class Payload(id: String, resource: Option[Any])
Usage: 用法:
validatePayload[String](Some(Payload("id", Some(5))))
I'd expect this to throw the illegal arg since I'm telling it to accept String and I'm passing in a Int. 我希望这会抛出非法的arg,因为我要告诉它接受String并传递一个Int值。 Why is it not?
为什么不呢?
My objective is to validate the payload been sent to an actor, the actor should only react to a specific type of resource and nothing else. 我的目标是验证已将有效负载发送给参与者,参与者仅应对特定类型的资源做出反应,而别无其他。 How can I fix this to accomplish that?
我该如何解决这个问题?
The simplest case is when you can use a ClassTag
(the limitation for this case is given below). 最简单的情况是可以使用
ClassTag
的情况(下面给出了这种情况的限制)。 For that case you can simply add a context bound to the function type definition, and it just works: 在这种情况下,您只需添加绑定到函数类型定义的上下文,它就可以工作:
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))))
At runtime it's pretty much equivalent to Java's instanceof
operator and a type cast. 在运行时,它几乎等效于Java的
instanceof
运算符和类型强制转换。
But the ClassTag
doesn't work for generic types. 但是
ClassTag
不适用于泛型类型。 For example, sequences with different element types aren't distinguished: 例如,不区分具有不同元素类型的序列:
// Doesn't throw
validatePayload[Seq[String]](Some(Payload("id", Some(Seq(1,2,3)))))
If you need to distinguish generic types, you'd have to use TypeTag
s. 如果需要区分泛型类型,则必须使用
TypeTag
。 You must know the type of resource, when you are creating the payload, and the payload must store its Type
or the TypeTag
of its type. 创建有效负载时, 必须知道资源的类型,并且有效负载必须存储其
Type
或该Type
的TypeTag
。
Here is an example: 这是一个例子:
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.")
}
}
}
}
Now it will distinguish generics: 现在它将区分泛型:
// Throws an error
validatePayload[Seq[String]](Some(Payload("id", Some(Seq(1,2,3)))))
But TypeTag
s rely on types known at compile time. 但是
TypeTag
依赖于编译时已知的类型。 So if resource
has type Any
before you create Payload
with it, the validatePayload[T]
will not throw only if T
is Any
. 因此,如果在使用
resource
创建Payload
之前resource
类型为Any
,则仅当T
为Any
validatePayload[T]
不会抛出。 And there are some other quirks: 还有其他一些怪癖:
// 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)))))
A more robust method is provided by a third-party library shapeless
. 第三方库
shapeless
提供了更强大的方法。 Here is an example: 这是一个例子:
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.")
}
}
}
}
Both don't throw now: 两者都不抛出:
validatePayload[Seq[Int]](Some(Payload("id", Some(List(1,2,3)))))
validatePayload[List[Int]](Some(Payload("id", Some(Seq(1,2,3)))))
Due to type erasure you can't check type like this, but ClassTag
is a workaround. 由于类型删除,您无法检查这种类型,但是
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"))
}
I simplified the code, if you don't need custom errors, it's less verbose for me at least. 我简化了代码,如果您不需要自定义错误,至少对我来说,它不会那么冗长。 Although if you want to stick to your code, only important parts from your problems view is declaring that the type
T
needs and implicit ClassTag[T]
which is declared like this [T: ClassTag]
and check if the type is valid here: 尽管如果您要坚持使用代码,那么从问题角度来看,只有重要的部分才声明类型
T
需要和隐式ClassTag[T]
,该声明像这样[T: ClassTag]
并在此处检查类型是否有效:
val c = implicitly[ClassTag[T]].runtimeClass
c.isInstance(res)
Here is a test 这是一个测试
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.