Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I want to get the type of a variable at runtime

I want to get the type of a variable at runtime. How do I do this?

like image 912
Incerteza Avatar asked Oct 15 '13 17:10

Incerteza


1 Answers

So, strictly speaking, the "type of a variable" is always present, and can be passed around as a type parameter. For example:

val x = 5 def f[T](v: T) = v f(x) // T is Int, the type of x 

But depending on what you want to do, that won't help you. For instance, may want not to know what is the type of the variable, but to know if the type of the value is some specific type, such as this:

val x: Any = 5 def f[T](v: T) = v match {   case _: Int    => "Int"   case _: String => "String"   case _         => "Unknown" } f(x) 

Here it doesn't matter what is the type of the variable, Any. What matters, what is checked is the type of 5, the value. In fact, T is useless -- you might as well have written it def f(v: Any) instead. Also, this uses either ClassTag or a value's Class, which are explained below, and cannot check the type parameters of a type: you can check whether something is a List[_] (List of something), but not whether it is, for example, a List[Int] or List[String].

Another possibility is that you want to reify the type of the variable. That is, you want to convert the type into a value, so you can store it, pass it around, etc. This involves reflection, and you'll be using either ClassTag or a TypeTag. For example:

val x: Any = 5 import scala.reflect.ClassTag def f[T](v: T)(implicit ev: ClassTag[T]) = ev.toString f(x) // returns the string "Any" 

A ClassTag will also let you use type parameters you received on match. This won't work:

def f[A, B](a: A, b: B) = a match {   case _: B => "A is a B"   case _ => "A is not a B" } 

But this will:

val x = 'c' val y = 5 val z: Any = 5 import scala.reflect.ClassTag def f[A, B: ClassTag](a: A, b: B) = a match {   case _: B => "A is a B"   case _ => "A is not a B" } f(x, y) // A (Char) is not a B (Int) f(x, z) // A (Char) is a B (Any) 

Here I'm using the context bounds syntax, B : ClassTag, which works just like the implicit parameter in the previous ClassTag example, but uses an anonymous variable.

One can also get a ClassTag from a value's Class, like this:

val x: Any = 5 val y = 5 import scala.reflect.ClassTag def f(a: Any, b: Any) = {   val B = ClassTag(b.getClass)   ClassTag(a.getClass) match {     case B => "a is the same class as b"     case _ => "a is not the same class as b"   } } f(x, y) == f(y, x) // true, a is the same class as b 

A ClassTag is limited in that it only covers the base class, but not its type parameters. That is, the ClassTag for List[Int] and List[String] is the same, List. If you need type parameters, then you must use a TypeTag instead. A TypeTag however, cannot be obtained from a value, nor can it be used on a pattern match, due to JVM's erasure.

Examples with TypeTag can get quite complex -- not even comparing two type tags is not exactly simple, as can be seen below:

import scala.reflect.runtime.universe.TypeTag def f[A, B](a: A, b: B)(implicit evA: TypeTag[A], evB: TypeTag[B]) = evA == evB type X = Int val x: X = 5 val y = 5 f(x, y) // false, X is not the same type as Int 

Of course, there are ways to make that comparison return true, but it would require a few book chapters to really cover TypeTag, so I'll stop here.

Finally, maybe you don't care about the type of the variable at all. Maybe you just want to know what is the class of a value, in which case the answer is rather simple:

val x = 5 x.getClass // int -- technically, an Int cannot be a class, but Scala fakes it 

It would be better, however, to be more specific about what you want to accomplish, so that the answer can be more to the point.

like image 67
Daniel C. Sobral Avatar answered Sep 24 '22 17:09

Daniel C. Sobral