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

时间:2015-10-09 17:55:14

标签: scala pattern-matching

我的方法:

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应该只对特定类型的资源做出反应,而不是其他任何事情。 我该如何解决这个问题呢?

2 个答案:

答案 0 :(得分:4)

ClassTag

最简单的情况是你可以使用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运算符和类型转换。

TypeTag

ClassTag不适用于泛型类型。例如,不区分具有不同元素类型的序列:

// Doesn't throw
validatePayload[Seq[String]](Some(Payload("id", Some(Seq(1,2,3)))))

如果您需要区分通用类型,则必须使用TypeTag s。在创建有效负载时,必须知道资源的类型,并且有效负载必须存储其类型的TypeTypeTag

以下是一个例子:

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