How to write "asInstanceOfOption" in Scala
Is it possible to write an "asInstanceOfOption" method that w开发者_开发知识库ould do what is intended by the following (bogus) code?
def asInstanceOfOption[T](o: Any): Option[T] =
if (o.isInstanceOf[T]) Some(o.asInstanceOf[T]) else None
EDIT Below is my original answer but you can accomplish this now with
def asInstanceOfOption[T: ClassTag](o: Any): Option[T] =
Some(o) collect { case m: T => m}
You could use manifests to get around the fact that the type T
is erased at compile time:
scala> import scala.reflect._
import scala.reflect._
scala> def asInstanceOfOption[B](x : Any)(implicit m: Manifest[B]) : Option[B] = {
| if (Manifest.singleType(x) <:< m)
| Some(x.asInstanceOf[B])
| else
| None
| }
asInstanceOfOption: [B](x: Any)(implicit m: scala.reflect.Manifest[B])Option[B]
Then this could be used:
scala> asInstanceOfOption[Int]("Hello")
res1: Option[Int] = None
scala> asInstanceOfOption[String]("World")
res2: Option[String] = Some(World)
You could even use implicit conversions to get this to be a method available on Any
. I think I prefer the method name matchInstance
:
implicit def any2optionable(x : Any) = new { //structural type
def matchInstance[B](implicit m: Manifest[B]) : Option[B] = {
if (Manifest.singleType(x) <:< m)
Some(x.asInstanceOf[B])
else
None
}
}
Now you can write code like:
"Hello".matchInstance[String] == Some("Hello") //true
"World".matchInstance[Int] == None //true
EDIT: updated code for 2.9.x, where one can't use Any
but only AnyRef
:
implicit def any2optionable(x : AnyRef) = new { //structural type
def matchInstance[B](implicit m: Manifest[B]) : Option[B] = {
if (Manifest.singleType(x) <:< m)
Some(x.asInstanceOf[B])
else
None
}
}
Here's an elaboration on oxbow_lake's updated answer, further updated to require Scala 2.10:
// Implicit value class
implicit class Castable(val obj: AnyRef) extends AnyVal {
def asInstanceOfOpt[T <: AnyRef : ClassTag] = {
obj match {
case t: T => Some(t)
case _ => None
}
}
}
This can be used by doing:
"Hello".asInstanceOfOpt[String] == Some("Hello") // true
"foo".asInstanceOfOpt[List[_]] == None // true
However as mentioned in other answers this doesn't work for primitives because of boxing issues, nor does it handle generics because of erasure. To disallow primitives, I restricted obj
and T
to extend AnyRef
. For a solution that handles primitives, refer to the answer for Matt R's followup question:
How to write asInstanceOfOpt[T] where T <: Any
Or use shapeless's Typeable, which handles primitives as well as many erasure cases:
Type casting using type parameter
At the time of the writing of oxbow_lakes's answer (late '09), I believe scala.util.Try
was not available, however, now (i.e. as of 2.10) I think scala.util.Try
is the preferred (or well at least nicer-looking) way of doing this:
scala> Try((3).asInstanceOf[String]).toOption
res0: Option[String] = None
scala> Try("hello".asInstanceOf[String]).toOption
res1: Option[String] = Some(hello)
This relies on exceptions, so probably not the preferred style for performance critical blocks of code.
精彩评论