How to write “asInstanceOfOption” in Scala

前端 未结 3 1564
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-30 22:36

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[         


        
相关标签:
3条回答
  • 2021-01-30 23:12

    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
      }   
    }
    
    0 讨论(0)
  • 2021-01-30 23:12

    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.

    0 讨论(0)
  • 2021-01-30 23:33

    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

    0 讨论(0)
提交回复
热议问题