我的应用程序需要一个可以添加到 any的参数提供程序 ,以允许传递任意数量的任何类型的参数。trait
class
trait Arg
case class NamedArg(key: String, value: Any) extends Arg
// I extend my classes with this trait
trait ArgsProvider {
val args: Seq[Arg]
lazy val namedArgs: Map[String, Any] = {
args.filter(_.isInstanceOf[NamedArg]).
map(_.asInstanceOf[NamedArg]).
map(arg => arg.key -> arg.value).toMap
}
...
}
然后我可以从使用它们中提取NamedArg
s如下args
ArgsProvider
key
trait ArgsProvider {
...
/*
* Method that takes in a [T: ClassTag] and a (key: String) argument
* (i) if key exists in namedArgs: Map[String, Any]
* - Returns Some(value: T) if value can be casted into T type
* - Throws Exception if value can't be casted into T type
* (ii) if key doesn't exist in namedArgs
* Returns None
*/
def getOptionalTypedArg[T: ClassTag](key: String): Option[T] = {
namedArgs.get(key).map { arg: Any =>
try {
arg.asInstanceOf[T]
} catch {
case _: Throwable => throw new Exception(key)
}
}
}
...
}
尽管它看起来非常不直观和冗长,但这种设计对我来说完美无缺。然而,在写某某的时候unit tests
,我最近发现了其中的一个重大漏洞:执行失败type-checking
。(或者至少这是我推断的)
更具体地说,当我尝试将提供的类型设置为错误类型时,它不会引发任何异常。例如:type-cast
arg
// here (args: Seq[NamedArg]) overrides the (args: Seq[Arg]) member of ArgsProvider
case class DummyArgsProvider(args: Seq[NamedArg]) extends ArgsProvider
// instantiate a new DummyArgsProvider with a single NamedArg having a String payload (value)
val dummyArgsProvider: DummyArgsProvider = DummyArgsProvider(Seq(
NamedArg("key-string-arg", "value-string-arg")
))
// try to read the String-valued argument as Long
val optLong: Option[Long] = dummyArgsProvider.getOptionalTypedArg[Long]("key-string-arg")
虽然人们会期望上面的代码throw
是Exception
; 令我沮丧的是,它运行良好并返回以下输出(on Scala
REPL
)
optLong: Option[Long] = Some(value-string-arg)
我的问题是:
- 为什么类型检查在这里失败?
- 一般来说,Scala 的类型检查在什么情况下会失败?
- 这个设计可以改进吗?
我正在使用
Scala 2.11.11
SBT 1.0.3