Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a generic cast function Scala

Tags:

generics

scala

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?

like image 390
Bastian Echterhölter Avatar asked Aug 14 '11 20:08

Bastian Echterhölter


People also ask

How do I create a generic class in Scala?

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.

What is generic function in Scala?

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.

How do you cast 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.

How do you change data types in Scala?

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.


2 Answers

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
}
like image 167
Ruediger Keller Avatar answered Oct 11 '22 03:10

Ruediger Keller


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>)
        // ...
like image 34
Ben James Avatar answered Oct 11 '22 02:10

Ben James