Scala的类型检查奇怪的失败

时间:2018-02-20 09:27:55

标签: scala typechecking scala-generics

我的应用程序需要一个参数提供程序 sum,可以添加到任何library(dplyr) library(tidyr) bookings %>% separate_rows(roomIDs, convert = TRUE) %>% left_join(rooms, by = c(roomIDs = 'ID')) %>% group_by(ID, numofRooms) %>% summarise(roomIDs = paste(roomIDs, collapse= '|'), numofbeds = sum(numofBeds)) # A tibble: 6 x 4 # Groups: ID [?] # ID numofRooms roomIDs numofbeds # <int> <int> <chr> <int> #1 1160545 1 1202544 10 #2 1160546 2 1202547|1202563 5 #3 1160547 2 1202562|1202564 9 #4 1160548 1 1202565 4 #5 1160549 1 1202566 7 #6 1160550 3 1203751|1203752|1203753 6 以允许传递 任意数量的参数输入

trait

然后,我可以使用他们的classtrait 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中提取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上)

  

optLong:Option [Long] = Some(value-string-arg)

我的问题是:

  • 为什么类型检查失败?
  • 在什么情况下,Scala的类型检查通常会失败?
  • 这个设计可以改进吗?

我正在使用

  • Scala
  • REPL

2 个答案:

答案 0 :(得分:3)

正如@AlexeyRomanov所说,as/isInstanceOf[T]没有使用ClassTag

您可以使用模式匹配,如果有ClassTag,则使用trait ArgsProvider { /* ... */ def getOptionalTypedArg[T: ClassTag](key: String): Option[T] = { namedArgs.get(key).map { case arg: T => arg case _ => throw new Exception(key) } } } 进行检查:

ClassTag

或者您可以直接使用import scala.reflect.classTag def getOptionalTypedArg[T: ClassTag](key: String): Option[T] = { namedArgs.get(key).map { arg => classTag[T].unapply(arg).getOrElse(throw new Exception(key)) } } 的方法:

ContactPhoto contactPhoto = contact.primaryPhoto();
QString photo = contactPhoto.originalPhoto();
//photo = file:///accounts/1000/pimdata/_startup_data/contacts/2/img-tnqpx0.jpg
if (!photo.isEmpty()){
    QFile file(photo);
    if (file.open(QIODevice::ReadOnly)) {
       qDebug() <<"file.readAll() IF" <<file.readAll() <<endl;
    }else{
       qDebug() <<"file.readAll() ELSE" <<endl;
    }
    vcardString += "PHOTO;JPEG;ENCODING=BASE64:" + (file.readAll() + "\n");
}

答案 1 :(得分:1)

您遇到类型擦除问题:Option[Long]实际存储字符串&#34; value-string-arg&#34;并且不关心它被删除的类型。

但是,如果你执行optLong.get,它会尝试将其强制转换为预期输出的Long。而且您将获得ClassCastException

只是一点意见:

替换

val namedArgs: Map[String, Any] = {...}

通过

val namedArgs: Map[String, Any] = args.collect{
  case NameArg(k, v) => k -> v
}(collection.breakout)

另外,在你的getOptionalTypedArg中,不要抓住所有Throwable。这是不好的做法(你可以捕获OutOfMemoryError和其他致命错误,你不应该这样做)。在您的情况下,您希望捕获ClassCastException。在其他情况下,如果您不确切知道哪个Throwable,请尝试使用NonFatal