Generics 编写一个通用的cast函数Scala
我试图实现编写一个方法,将任意值强制转换为特定类型并返回选项,而不是抛出instanceOf之类的异常。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
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