【问题标题】:Scala asInstanceOf generic type does not fail in TryScala asInstanceOf 泛型类型在 Try 中不会失败
【发布时间】:2014-08-10 03:44:05
【问题描述】:

尝试将String 转换为Double 显然会失败:

scala> Try("abc".asInstanceOf[Double])
res11: scala.util.Try[Double] = Failure(java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Double)

但是,如果我将上述定义为一个函数:

scala> def convertAsTypeOf[T](anyValue: Any): Try[T] = Try(anyValue.asInstanceOf[T])
convertAsTypeOf: [T](anyValue: Any)scala.util.Try[T]

奇怪的是,当我尝试将 String 转换为 Double 时,它会返回 Success:

scala> convertAsTypeOf[Double]("abc")
res10: scala.util.Try[Double] = Success(abc)

如果我尝试从 Success 中获取值,则会收到以下异常:

scala> convertAsTypeOf[Double]("abc").get
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Double
        at scala.runtime.BoxesRunTime.unboxToDouble(BoxesRunTime.java:119)

为什么会这样?有没有办法解决这个问题并在泛型函数中生成asInstanceOf[T]

【问题讨论】:

  • 有人应该在用例中提及import shapeless.syntax.typeable._ ; "abc".cast[Double]

标签: scala generics casting


【解决方案1】:

由于类型擦除,T 类型在运行时是未知的,因此asInstanceOf 无法实际检查它所做的转换。事实上,它编译为无操作。但是,当.get 最终完成时,将转换为Double,并且由于此处已知类型,我们可以得到ClassCastException

如果您将convertAsTypeOf 更改为使用ClassTag

def convertAsTypeOf[T](anyValue: Any)(implicit tag: ClassTag[T]): Try[T] =
  Try(tag.runtimeClass.cast(anyValue).asInstanceOf[T])

然后你会在你期望的时候得到错误:

scala> convertAsTypeOf[Double]("abc")
res1: scala.util.Try[Double] = Failure(java.lang.ClassCastException: Cannot cast java.lang.String to double)

ClassTag 在运行时表示 T 类型,允许对照它检查值。

【讨论】:

  • 您好,感谢您的回答。它不适用于 Double 或 Int 参数。 scala> convertAsTypeOf[Double](32.2) res86: scala.util.Try[Double] = Failure(java.lang.ClassCastException: Cannot cast java.lang.Double to double)
【解决方案2】:

发生这种情况的原因是类型擦除,这使得泛型 T 的 asInstanceOf[T] 无用。我不知道为什么编译器没有发出任何警告。

您可以使用 ClassTag 强制进行检查。像这样的:

import reflect.ClassTag

def convertAsTypeOf[T : ClassTag](anyValue: Any): Try[T] = Try {
    anyValue match { case t: T => t }
}

这是可行的,因为编译器将在大小写匹配中使用隐式 ClassTag(如果可用,则生成警告)。

【讨论】:

  • asInstanceOf[T] 让您在知道编译器不知道的事情时重新获得类型安全。
  • 如果T 被删除,类型安全是否需要在某处进行运行时检查?编译器如何决定将检查放在哪里?
  • 当您将值用作某种类型时会发生这种情况。在 REPL 中,使用 :javap -prv - 比较 val x = identity(2.0)val x: Any = identity(2.0)。或者f[A](a: A) = 42.asInstanceOf[A],它会在第一种情况下抛出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-15
  • 1970-01-01
  • 1970-01-01
  • 2020-09-10
  • 1970-01-01
相关资源
最近更新 更多