2

I have instance and type and want to get Some in case casting is possible and None if not.

Currently I use my own implementation:

def tryCast[T](o: Any)(implicit manifest: Manifest[T]): Option[T] = {
  val clazz = manifest.runtimeClass.asInstanceOf[Class[T]]
  if (clazz.isAssignableFrom(o.getClass)) {
    Some(o.asInstanceOf[T])
  } else {
    None
  }
}

Is there any method in standard library that I can use instead?

talex
  • 17,973
  • 3
  • 29
  • 66

3 Answers3

6

As far as I know, there is no standard implementation of this function. You need to use your own implementation. However, Manifest is deprecated, and you should use ClassTag instead. Further, when an implicit ClassTag is present, the compiler lets you use pattern matching instead of manually checking classes.

def tryCast[To: ClassTag](x: Any): Option[To] = x match {
  case thing: To => Some(thing)
  case _ => None
}

The above method can also be written as follows:

//            "To: ClassTag" desugars to this v parameter, but it has no name
def tryCast[To](x: Any)(implicit tag: ClassTag[To]): Option[To] = x match {
  case thing: To => Some(thing)
  case _ => None
}
HTNW
  • 27,182
  • 1
  • 32
  • 60
  • Unfortunately pattern matching doesn't work because of type erasure. – talex Dec 09 '16 at 17:25
  • I was wrong. `ClassTag` helps. I need to learn scala better because I have no idea what `[T:X]` mean. – talex Dec 09 '16 at 17:34
  • 1
    1) When a implicit ClassTag[T] is present, the compiler automatically resolves pattern matching on T with it. 2) T: M in a type parameter means that a implicit parameter of type M[T] is added to the method, without a name. – HTNW Dec 09 '16 at 17:39
  • @talex `def foo[T: X]` is syntax sugar for `def foo[T](implicit x: X[T])`. X could be known as a "typeclass", which should be a good jumping-off point for googling. – Dylan Dec 09 '16 at 17:47
  • Note that this only works modulo type erasure. `tryCast[List[String]](List(1, 2, 3): Any)` – Michael Zajac Dec 09 '16 at 20:14
  • @MichaelZajac of course, a normal asInstanceOf won't catch that either, so it's not any safer than this. – HTNW Dec 09 '16 at 21:19
  • Unfortunately I can't accept this as answer because I look for standard function (which isn't exist as I understand), but thank you all I understand Scala little better now. – talex Dec 10 '16 at 19:05
1

I am not sure of any function like that but you could use Try along with .toOption:

import scala.util.Try
val name: Any = "tyler"
val name: Any = "tyler"
val maybeString: Option[String] = Try(name.asInstanceOf[String]).toOption
val maybeInt = Try(name.asInstanceOf[Int]).toOption

println(maybeString)
println(maybeInt)

Output

Some(tyler)
None

You could take this a step further with an enhanced type with an implicit conversion.

Tyler
  • 17,669
  • 10
  • 51
  • 89
  • I look for method, not short expression and if i wrap your code in method it won;t be any better than my. – talex Dec 09 '16 at 17:28
  • I'm not sure if it's a good idea to catch exception just to check that the object is not of given type. – adamwy Dec 09 '16 at 17:43
  • Generating and catching an exception is unnecessarily costly for this. – Chris Martin Dec 09 '16 at 17:44
  • @ChrisMartin Could you elaborate a little further on the cost associated with throwing an exception? – Tyler Dec 09 '16 at 17:45
  • An exception is a somewhat large data structure, since it contains a stack trace. In practice I suppose the performance depends on what the JIT decides to do - perhaps it may be clever enough to see that the exception is never used - but why take the chance? – Chris Martin Dec 09 '16 at 17:49
  • 1
    This answer (http://stackoverflow.com/a/299315/409976) discusses an exception's impact on performance. – Kevin Meredith Dec 09 '16 at 18:22
  • This may be a case where exceptions are used as part of the control flow (since we can check if casting is possible w/o exceptions), which is [an antipattern](http://softwareengineering.stackexchange.com/a/189225/185615) – evan.oman Dec 09 '16 at 19:12
1

You maybe want the TypeTag, like:

def tryCast[A](o: A)(implicit tpe: Type, ta: TypeTag[A]): Option[A] = ta.tpe =:= tpe match {
      case true => Some(o)
      case false => None
}
implicit val tag = typeOf[Int]
val cast: Option[Int] = tryCast(1)
val cast2: Option[String] = tryCast("hello")
println(cast)
println(cast2)

OutPut:

>Some(1)
>None

implicit tpe: Type is your wanted match type, and use ta: TypeTag[A] implicit TypeTag get your parameter type.

for this way, no cast, more safe.

chengpohi
  • 14,064
  • 1
  • 24
  • 42
  • @MichaelZajac, this question wants to check whether parameter type is `T` by `implicit Manifest`, I think it already know which type, it wants to match, Is it right? – chengpohi Dec 09 '16 at 18:03
  • If `o` is `Any`, then you'll get a `TypeTag[Any]`--that's what I mean. For example, `val o: Any = "a" ; tryCast[String](o)` will not work. Comparing type tags only makes sense if a type tag was captured for `o` somewhere else, which based on the OP's signature, doesn't seem to happen. – Michael Zajac Dec 09 '16 at 20:11