I am trying to achieve to write a method that casts a value of Any to a specific type and returns option instead of throwing an exception like instanceOf. Scala does not behave like i have expected it:
def cast[A](value: Any): Option[A] =
{
try
{
Some(value.asInstanceOf[A])
} catch
{
case e: Exception => None
}
}
The test:
val stringOption: Option[String] = cast[String](2)
stringOption must beNone
fails with the Error
java.lang.Exception: 'Some(2)' is not None
Somebody has an idea why?
Defining a generic classGeneric classes take a type as a parameter within square brackets [] . One convention is to use the letter A as type parameter identifier, though any parameter name may be used. This implementation of a Stack class takes any type A as a parameter.
The classes that takes a type just like a parameter are known to be Generic Classes in Scala. This classes takes a type like a parameter inside the square brackets i.e, [ ]. This classes are utilized explicitly for the progress of the collection classes in Scala.
Type Casting in Scala is done using the asInstanceOf[] method. This perspective is required in manifesting beans from an application context file. It is also used to cast numeric types. It can even be applied in complex codes like communicating with Java and sending it an array of Object instances.
Scala provides three main ways to convert the declared type of an object to another type: Value type casting for intrinsic types such as Byte, Int, Char, and Float. Type casting via the asInstanceOf[T] method. Pattern matching to effect type casting using the match statement.
Erasure rains on your parade here. Therefore at runtime the type A is not known anymore and asInstanceOf[A]
is compiled to a no-op. It just makes the compiler believe that the resulting value is of type A, but that is not actually ensured at runtime.
You can use Scala's manifests to work around it, though. Unfortunately the JVM's handling of primitive types / boxing forces us to do some extra work.
The following works, although it doesn't handle "weak conformance" of types, meaning that e.g. an Int is not considered a Long, so cast[Long](42)
returns None
.
def cast[A : Manifest](value: Any): Option[A] = {
val erasure = manifest[A] match {
case Manifest.Byte => classOf[java.lang.Byte]
case Manifest.Short => classOf[java.lang.Short]
case Manifest.Char => classOf[java.lang.Character]
case Manifest.Long => classOf[java.lang.Long]
case Manifest.Float => classOf[java.lang.Float]
case Manifest.Double => classOf[java.lang.Double]
case Manifest.Boolean => classOf[java.lang.Boolean]
case Manifest.Int => classOf[java.lang.Integer]
case m => m.erasure
}
if(erasure.isInstance(value)) Some(value.asInstanceOf[A]) else None
}
This is because of type erasure. At runtime, A
in Option[A]
is not known, so you are permitted to store a Some(3)
in a variable of type Option[String]
.
The exception will occur when the value inside the option is accessed:
scala> val result = cast[String](2)
result: Option[String] = Some(2)
scala> result.get
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at .<init>(<console>:10)
at .<clinit>(<console>)
// ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With