How to write asInstanceOfOpt[T] where T <: Any

后端 未结 3 846
借酒劲吻你
借酒劲吻你 2021-02-06 03:41

There\'s a handy implementation of asInstanceOfOpt, a safe version of asInstanceOf, given in the answer to How to write "asInstanceOfOption"

相关标签:
3条回答
  • 2021-02-06 03:58

    You could use shapeless's Typeable from Miles Sabin:

    Type casting using type parameter

    It handles primitives and boxing:

    scala> import shapeless._; import syntax.typeable._
    import shapeless._
    import syntax.typeable._
    
    scala> 1.cast[Int]
    res1: Option[Int] = Some(1)
    
    scala> 1.cast[String]
    res2: Option[String] = None
    
    scala> "hello".cast[String]
    res4: Option[String] = Some(hello)
    
    scala> "foo".cast[Int]
    res5: Option[Int] = None
    

    You can see the source here to see how it's written:

    https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/typeable.scala

    0 讨论(0)
  • 2021-02-06 04:05

    If you look in the Scala API the function singleType takes a parameter of type AnyRef. I don't really know the background for this decision, but it seems you need to work around it. Instead of using the method singleType I'd suggest using the classType method which basically can make a manifest for any class. It'll take a bit more code, but it could look something like this:

    class WithAsInstanceOfOpt(obj : Any) {
      def asInstanceOfOpt[B : Manifest] : Option[B] =   // [B : Manifest] is shorthand for [B](implicit m : Manifest[B])
        if (Manifest.classType(manifest, obj.getClass) <:< manifest)
          Some(obj.asInstanceOf[B])
        else None
    }
    
    0 讨论(0)
  • 2021-02-06 04:10

    Here's working code for 2.9.x. It will give deprecation warnings for 2.10.x, but using ClassTag instead of Manifest and runtimeClass instead of erasure will fix them.

    //Precondition: classS must have been produced through primitiveToBoxed, because v will be boxed.
    def ifInstanceOfBody[T, S](v: T, classS: Class[_]): Option[S] = {
      if (v == null || !classS.isInstance(v))
        None
      else
        Some(v.asInstanceOf[S])
    }
    object ClassUtil {
      import java.{lang => jl}
    
      private val primitiveToBoxedMap = Map[Class[_], Class[_]](
        classOf[Byte] -> classOf[jl.Byte],
        classOf[Short] -> classOf[jl.Short],
        classOf[Char] -> classOf[jl.Character],
        classOf[Int] -> classOf[jl.Integer],
        classOf[Long] -> classOf[jl.Long],
        classOf[Float] -> classOf[jl.Float],
        classOf[Double] -> classOf[jl.Double],
        classOf[Boolean] -> classOf[jl.Boolean],
        classOf[Unit] -> classOf[jl.Void]
      )
      def primitiveToBoxed(classS: Class[_]) =
        primitiveToBoxedMap.getOrElse(classS, classS)
    }
    class IfInstanceOfAble[T](v: T) {
      def asInstanceOfOpt[S](implicit cS: Manifest[S]): Option[S] =
        ifInstanceOfBody[T, S](v, ClassUtil.primitiveToBoxed(cS.erasure))
    }
    implicit def pimpInstanceOf[T](t: T) = new IfInstanceOfAble(t)
    

    Testing results:

    scala> 1.asInstanceOfOpt[Int]
    res9: Option[Int] = Some(1)
    
    scala> "".asInstanceOfOpt[String]
    res10: Option[String] = Some()
    
    scala> "foo".asInstanceOfOpt[String]
    res11: Option[String] = Some(foo)
    
    scala> 1.asInstanceOfOpt[String]
    res12: Option[String] = None
    
    scala> "".asInstanceOfOpt[Int]
    res13: Option[Int] = None
    

    The code is slightly more verbose than needed here, mostly because I took it from an existing codebase of mine where I reuse ifInstanceOfBody elsewhere. Inlining into asInstanceOfOpt would fix that and shorten the code somewhat, but most of it is for primitiveToBoxedMap, and trust me that I could not find something like that available in the Scala standard library.

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