27

Is it possible to write an "asInstanceOfOption" method that would 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 
Matt R
  • 9,892
  • 10
  • 50
  • 83

3 Answers3

30

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
  }   
}
Arne Claassen
  • 14,088
  • 5
  • 67
  • 106
oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
  • 2
    Only one observation; the "structural type" uses reflection to invoke the `matchInstance` method. If performance is more critical you could use a standard conversion to a non-structural type – oxbow_lakes Nov 26 '09 at 14:28
  • 1
    The `matchInstance` method is _not_ invoked using reflection, since that is _not_ a structural type but just an anonymous class. – Kim Stebel May 08 '11 at 08:37
  • 1
    It seems this now only works with AnyRef -- I've posted a question about Any: http://stackoverflow.com/questions/7873936/how-to-write-asinstanceofoptt-where-t-any – Matt R Oct 24 '11 at 10:01
  • Kim Stebel: sorry, you're factually incorrect. Luckily, Scala 2.10 with the -feature option gives a warning about this. – Blaisorblade Sep 02 '12 at 17:16
  • To get the warning you must use the added method, as in `"".matchInstance[String]`. – Blaisorblade Sep 02 '12 at 18:57
  • @KimStebel: see my comment above (I had forgot how to tag you). To clarify my comment: matchInstance is part of an anonymous class, but the return type of a method cannot mention anonymous classes, so its type is just `Object {def matchInstance[B](implicit m: Manifest[B]): Option[B]}`. I also answered MattR's question; there I avoid the problem with reflective calls by having the implicit conversion produce a class `IfInstanceOfAble` with the desired method. – Blaisorblade Sep 02 '12 at 19:33
  • Can somebody clarify the final full solution? The `Some(x) collect...` solution - it needs to be wrapped inside a def. Does that def require a `ClassTag`? – tksfz Mar 19 '14 at 00:22
3

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

Community
  • 1
  • 1
tksfz
  • 2,932
  • 1
  • 23
  • 25
1

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.

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • 2
    Why its this preferable to oxbow_lake's `Some(x) collect { case m : MyType => m }`? Try will raise an exception and handle it, which seems a bit excessive... – The Archetypal Paul Feb 18 '14 at 22:15
  • @Paul: if you mean performance, then you might be right; I just think it's more readable and easier to type as well. – Erik Kaplun Feb 18 '14 at 22:17
  • OK. Readability is always personal of course, but I think the collect is clear (says more directly what is intended) – The Archetypal Paul Feb 18 '14 at 22:31
  • @Paul: for me *"tentatively convert `x` to `SomeType`"* is more readable... because you're not collecting anything... `collect` is just a technical word in this case, and so is `Some`... in my example, only `toOption` is technical. – Erik Kaplun Feb 18 '14 at 23:44