简体   繁体   English

为什么我的验证在检查输入类型时不抛出异常?

[英]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? 我该如何解决这个问题?

ClassTag 类标签

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运算符和类型强制转换。

TypeTag 类型标签

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或该TypeTypeTag

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 ,则仅当TAny 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)))))

cast from shapeless 从无形铸造

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.

相关问题 如果Scala的==调用等于,那么为什么不抛出异常? - If Scala's == calls equals, why doesn't this throw an exception? 为什么asInstanceOf不会抛出ClassCastException? - Why asInstanceOf doesn't throw a ClassCastException? Scala宏注释-当我将其用作类型参数时,为什么类似乎没有更新? - Scala Macro Annotations - Why doesn't my class appear to be updated when I use it as a type parameter? 为什么在 Spark 中传递格式错误的时区字符串时 from_utc_timstamp 不抛出错误? - Why doesn't from_utc_timstamp throw an error when passed a malformed timezone string in Spark? Slick默默地保存失败,并且不会引发异常 - Slick silently fails to save and doesn't' throw an exception 当foreach添加元素时,scala MutableList为什么不抛出异常 - scala MutableList when foreach add element why not throw Exception 为什么null.asInstanceOf [Int]不抛出NullPointerException? - why doesn't null.asInstanceOf[Int] throw a NullPointerException? 异常抛出类型为“Nothing”? - An exception throw has type `Nothing`? 为什么AdminClient不抛出异常? - Why does the AdminClient not throw an exception? 使用@NotEmpty验证Scala的地图时,引发javax.validation.UnexpectedTypeException:HV000030异常 - Throw javax.validation.UnexpectedTypeException: HV000030 exception when validating Scala's Map with @NotEmpty
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM