Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala cast to a variable type

Tags:

casting

scala

I have the following code to cast value to the type of default:

def fct[T](value: Any, default: T): T = {
    val result = value.asInstanceOf[T]
    println(result, result.getClass.getName, result.isInstanceOf[T])
    result
}

val res = fct("foo", 42)

Which result is:

(foo,java.lang.String,true)
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at scala.runtime.BoxesRunTime.unboxToInt(Test.sc2.tmp)
    at #worksheet#.res$lzycompute(Test.sc2.tmp:7)
    at #worksheet#.res(Test.sc2.tmp:7)
    at #worksheet#.#worksheet#(Test.sc2.tmp:7)

Question is: why the println is displayed? The cast should fail. I tried to try / catch the 3 lines but no exception are raised during the function call.

like image 831
Benjamin Avatar asked Dec 18 '22 12:12

Benjamin


1 Answers

Because of type erasure, it's impossible to actually implement value.{as/is}InstanceOf[T] where T is a type parameter. Scala's designers decided it should still compile, but value.asInstanceOf[T] is actually a no-op (just like the equivalent (T) value in Java), while value.isInstanceOf[T] always returns true (in Java, value instanceOf T is a compiler error). (Since it never does what you want, I'd strongly like to see a warning instead, at least for isInstanceOf.)

But Scala allows to do what you want using ClassTag:

import scala.reflect.ClassTag

def fct[T](value: Any, default: T)(implicit tag: ClassTag[T]): T = {
    val result = (tag match {
      case ClassTag.Int => value.asInstanceOf[Int]
      ... same for other primitives
      case _ => tag.runtimeClass.cast(value)
    }).asInstanceOf[T]
    println(result, result.getClass.getName, tag.runtimeClass.isInstance(result))
    result
}

(You still need asInstanceOf[T] because tag.runtimeClass usually returns the same class T represents but not always, so its static return type has to be Class[_] and cast returns Any).

However, pattern-matching : T handles presence of a ClassTag automatically and already handles boxing, so

def fct[T](value: Any, default: T)(implicit tag: ClassTag[T]): T = value match {
    case result: T => result
    case _ => default // or whatever you want to do if value is not a T
}

is the better way to do this.

like image 75
Alexey Romanov Avatar answered Dec 21 '22 03:12

Alexey Romanov