Scala:谁能解释一下?
Posted
技术标签:
【中文标题】Scala:谁能解释一下?【英文标题】:Scala: who can explain this? 【发布时间】: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
,如果T
是Unit
则可以,如果T
是其他类型则不可以。
【问题讨论】:
【参考方案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
解决方法将不起作用。它会默默工作,然后失败,再次抛出ClassCastException
。 runtimeClass
不返回 Class[T]
是有原因的。
ClassTag
hackaround 在这种情况下效果很好,因为只有 Unit
类型需要特殊处理,所有其他类型(包括参数化类型)都应该导致 IllegalArgumentException
。也可以将implicit ClassTag
移动到get
定义中。以上是关于Scala:谁能解释一下?的主要内容,如果未能解决你的问题,请参考以下文章