Generics 编写一个通用的cast函数Scala

Generics 编写一个通用的cast函数Scala,generics,scala,Generics,Scala,我试图实现编写一个方法,将任意值强制转换为特定类型并返回选项,而不是抛出instanceOf之类的异常。Scala的行为与我预期的不同: def cast[A](value: Any): Option[A] = { try { Some(value.asInstanceOf[A]) } catch { case e: Exception => None } } 测试: val stringOption: Option[String] = cast[St

我试图实现编写一个方法,将任意值强制转换为特定类型并返回选项,而不是抛出instanceOf之类的异常。Scala的行为与我预期的不同:

def cast[A](value: Any): Option[A] =
{
  try
  {
    Some(value.asInstanceOf[A])
  } catch
  {
    case e: Exception => None
  }
}
测试:

val stringOption: Option[String] = cast[String](2)
stringOption must beNone
由于错误而失败

java.lang.Exception: 'Some(2)' is not None

有人知道为什么吗?

这是因为类型擦除。在运行时,
Option[A]
中的
A
是未知的,因此允许您将
Some(3)
存储在
Option[String]
类型的变量中

当访问选项内的值时,将发生异常:

scala> val result = cast[String](2)
result: Option[String] = Some(2)

scala> result.get
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
        at .<init>(<console>:10)
        at .<clinit>(<console>)
        // ...
scala>val result=cast[String](2)
结果:选项[String]=Some(2)
scala>result.get
java.lang.ClassCastException:java.lang.Integer不能转换为java.lang.String
在。(:10)
在
// ...

你的阅兵式上到处都是擦除雨。因此,在运行时类型A不再为人所知,而
asInstanceOf[A]
被编译为no-op。这只会使编译器相信结果值是类型A,但在运行时实际上无法确保这一点

不过,您可以使用Scala的清单来解决这个问题。不幸的是,JVM对原语类型/装箱的处理迫使我们做一些额外的工作

下面的方法是有效的,尽管它不处理类型的“弱一致性”,这意味着例如Int不被视为Long,因此
cast[Long](42)
返回
None

def cast[A : Manifest](value: Any): Option[A] = {
  val erasure = manifest[A] match {
    case Manifest.Byte => classOf[java.lang.Byte]
    case Manifest.Short => classOf[java.lang.Short]
    case Manifest.Char => classOf[java.lang.Character]
    case Manifest.Long => classOf[java.lang.Long]
    case Manifest.Float => classOf[java.lang.Float]
    case Manifest.Double => classOf[java.lang.Double]
    case Manifest.Boolean => classOf[java.lang.Boolean]
    case Manifest.Int => classOf[java.lang.Integer]
    case m => m.erasure
  }
  if(erasure.isInstance(value)) Some(value.asInstanceOf[A]) else None
}

我刚才做了几乎相同的事情,使用Scala2.10、TypeTags(由于类型擦除)和来自scalaz的ValidationNEL:

import scala.reflect.runtime.universe._

def as[T: TypeTag](term: Any): ValidationNEL[String, T] =
  if (reflect.runtime.currentMirror.reflect(term).symbol.toType <:< typeOf[T])
    term.asInstanceOf[T].successNel[String]
  else
    ("Cast error: " + term + " to " + typeOf[T]).failNel[T]
导入scala.reflect.runtime.universe_
def as[T:TypeTag](术语:Any):ValidationNEL[String,T]=

if(reflect.runtime.currentmirr.reflect(term).symbol.toType)将整数值转换为字符串会导致异常,并且该方法应返回None,但事实并非如此。我使用scala 2.9.0-1是,它会返回一些(2)但是…不是。尝试
get
该值会引发异常,但是
getOrElse
是可以的。是的,没错,我希望该异常会发生在cast方法中。它与类型擦除有什么关系吗?为什么
getOrElse(42)
不会生成此异常,而是计算为2?返回类型为
选项[A]。getOrElse[B] 
必须是
a
B
的超类型。在
String
Int
的情况下,返回类型是
Any
,您当然可以将
2
强制转换为
Any
。如果您尝试
getOrElse(“42”)
您将得到一个
ClassCastException
,因为返回类型将是
String
。这是有意义的,所以没有简单的方法以通用的方式编写这样的方法?我将回答“您可能可以使用清单来完成此操作”-好吧,请参阅Ruediger的回答:)这不是真的-asInstanceOf总是编译为非基元类型和泛型的checkcast操作
  def as[T: TypeTag](term: Any): Option[T] =
  if (reflect.runtime.currentMirror.reflect(term).symbol.toType <:< typeOf[T])
    Some(term.asInstanceOf[T])
  else
    None