【问题标题】:Scala: who can explain this?Scala:谁能解释一下?
【发布时间】:2013-09-12 19:47:46
【问题描述】:

考虑以下 Scala 代码:

case class Data[T](value: Option[T]) {
  def get: T = try {
    doGet
  } catch {
    case e: Exception => throw new IllegalArgumentException
  }

  def doGet: T = value match {
    case Some(v) => v
    case None => ().asInstanceOf[T]
  }
}

Data[Unit](None).get
Data[Integer](None).get // which exception is thrown here?

[剧透]这是一个ClassCastException;谁能解释为什么它没有被IllegalArgumentException 捕获和替换?

PS:为了抢占我为什么要这样做的任何问题:这是一些代码的简化版本,它使用 json4s 将一些字符串解析为Option[T];如果解析失败则返回None,如果TUnit 则可以,如果T 是其他类型则不可以。

【问题讨论】:

    标签: scala generics


    【解决方案1】:

    说明

    这里没有抛出异常:

    ().asInstanceOf[T]
    

    因为这是一个未经检查的转换 - JVM 无法验证是否可以将 () 转换为 T,因为由于类型擦除,它没有关于 T 的信息。

    相反,这里抛出异常

    Data[Integer](None).get
    

    因为get 的结果被转换为Integer,这是 JVM 可以验证的。所以,ClassCastException 实际上被抛到了get 之外。

    顺便说一句,javac 总是警告未经检查的演员表,我不知道为什么 scalac 没有。

    解决方法

    在某种程度上,可以使用ClassTag 和基于反射的转换来解决类型擦除问题:

    import scala.reflect.{ClassTag, classTag}
    
    case class Data[T: ClassTag](value: Option[T]) {
      def get: T = try {
        doGet
      } catch {
        case e: Exception => throw new IllegalArgumentException
      }
    
      def doGet: T = value match {
        case Some(v) => v
        case None => classTag[T].runtimeClass.asInstanceOf[Class[T]].cast(())
      }
    }
    

    解决方法

    对于这个用例,您可以直接检查ClassTag

    scala> case class Data[T](value: Option[T])(implicit t: ClassTag[T]) {
         | def get: T = value getOrElse (t match {
         |   case ClassTag.Unit => ().asInstanceOf[T]
         |   case _ => throw new IllegalArgumentException
         | })
         | }
    defined class Data
    
    scala> Data[Unit](None)
    res6: Data[Unit] = Data(None)
    
    scala> .get
    
    scala> Data[Int](None).get
    java.lang.IllegalArgumentException
    

    【讨论】:

    • 啊哈!擦除!这就是关键。
    • 能否修改代码使get抛出异常?
    • 请注意,如果您尝试这种基于反射的强制转换,例如,将List[Int] 转换为List[String],您的ClassTag 解决方法将不起作用。它会默默工作,然后失败,再次抛出ClassCastExceptionruntimeClass 不返回 Class[T] 是有原因的。
    • ClassTag hackaround 在这种情况下效果很好,因为只有 Unit 类型需要特殊处理,所有其他类型(包括参数化类型)都应该导致 IllegalArgumentException。也可以将implicit ClassTag 移动到get 定义中。
    猜你喜欢
    • 2017-04-07
    • 2015-12-21
    • 2015-11-23
    • 2013-05-23
    • 2017-07-23
    • 1970-01-01
    • 1970-01-01
    • 2020-06-16
    • 2017-01-25
    相关资源
    最近更新 更多