2011-08-14 36 views
17

我正试图实现一种方法,将任何值都转换为特定类型并返回选项,而不是抛出像instanceOf这样的异常。 Scala没有表现得像我期望的那样:编写一个通用的强制转换函数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 

有人有一个想法,为什么?

+0

将整型值转换为字符串应该导致异常,并且方法应该返回None,但那不是这种情况。我使用Scala 2.9.0-1 –

+0

是的,它返回一些(2)但是...不是。试图“get”这个值会抛出异常,但getOrElse没问题。 – 2011-08-14 20:30:33

+0

是的,我期望在cast方法中发生异常。 –

回答

21

您游行擦除降雨在这里。因此在运行时,类型A不再是已知的,并且asInstanceOf[A]被编译为空操作。它只是让编译器相信所得到的值是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 
} 
+0

这不是真的 - asInstanceOf总是编译为非基元类型和泛型的检查操作 – dk14

5

这是因为类型擦除。在运行时,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>) 
     // ... 
+0

为什么'getOrElse(42)'不生成这个异常,但评估为2? – 2011-08-14 20:34:29

+2

“Option [A] .getOrElse [B]”的返回类型必须是“A”和“B”的超类型。在'String'和'Int'的情况下,返回类型是'Any',你当然可以将'2'投到'Any'。如果你尝试'getOrElse(“42”)''你会得到'ClassCastException',因为返回类型是'String'。 –

+0

有道理,所以没有简单的方法以通用的方式编写这样的方法吗? –

2

我做了几乎同样的事情就在刚才,使用Scala 2.10,类型标签(因为类型擦除)和ValidationNEL从scalaz:

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] 

随着期权,而不是验证它是这样的:

def as[T: TypeTag](term: Any): Option[T] = 
    if (reflect.runtime.currentMirror.reflect(term).symbol.toType <:< typeOf[T]) 
    Some(term.asInstanceOf[T]) 
    else 
    None 

我得到了我的相关信息在这里:How to know if an object is an instance of a TypeTag's type?Runtime resolution of type arguments using scala 2.10 reflection

相关问题