I want to get the type of a variable at runtime

165,258

Solution 1

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.

Solution 2

I think the question is incomplete. if you meant that you wish to get the type information of some typeclass then below:

If you wish to print as you have specified then:

scala>  def manOf[T: Manifest](t: T): Manifest[T] = manifest[T]
manOf: [T](t: T)(implicit evidence$1: Manifest[T])Manifest[T]

scala> val x = List(1,2,3)
x: List[Int] = List(1, 2, 3)

scala> println(manOf(x))
scala.collection.immutable.List[Int]

If you are in repl mode then

scala> :type List(1,2,3)
List[Int]

Or if you just wish to know what the class type then as @monkjack explains "string".getClass might solve the purpose

Solution 3

If by the type of a variable you mean the runtime class of the object that the variable points to, then you can get this through the class reference that all objects have.

val name = "sam";
name: java.lang.String = sam
name.getClass
res0: java.lang.Class[_] = class java.lang.String

If you however mean the type that the variable was declared as, then you cannot get that. Eg, if you say

val name: Object = "sam"

then you will still get a String back from the above code.

Solution 4

i have tested that and it worked

val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}

Solution 5

instance.getClass

above method return path of instance's Class. to get the exact Class name of the instance, try this:

instance.getClass.getSimpleName

here is the example: enter image description here

Share:
165,258

Related videos on Youtube

Incerteza
Author by

Incerteza

Alex is here: https://gildedhonour.co

Updated on May 19, 2021

Comments

  • Incerteza
    Incerteza almost 3 years

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

  • Readren
    Readren almost 8 years
    The example code you wrote after "But this will:" is confusing. It compiles but the result is not the one you show in the comments. Both calls return the same result: "A is a B". Because the value 5 is both an instance of Int and an instance of Any. Apart from that, your explanation was perfect :)
  • Daniel C. Sobral
    Daniel C. Sobral almost 8 years
    @Readren The value isn't tested for, the class is. Int is Any, but Any is not Int. It works on Scala 2.10, and it should work on Scala 2.11, and I don't know why it isn't.
  • Readren
    Readren almost 8 years
    It scares me to contradict an eminence like you, but the code a match { case _: B => ... test the type of the actual value of the variable a, not the type of the variable a. You are right in that it returns what you say in scala 2.10.6. But it should be a bug. In scala 2.11.8 the type of the actual value is tested, as it should.
  • David Arenburg
    David Arenburg over 7 years
    You can also do name.getClass.getSimpleName for a more readable output
  • marcin_koss
    marcin_koss about 7 years
    Very nice coverage on differences between ClassTag and TypeTag, just what I was looking for.
  • ChiMo
    ChiMo almost 6 years
    Is there a way to null check this?
  • Peter Krauss
    Peter Krauss over 4 years
    for readers: this is the most useful solution. As in Javascript typeof x, here manOf(x) say the data type!
  • Ravinder Payal
    Ravinder Payal about 3 years
    It doesn't tell the sub types of class. As mentioned above, for both List[String] and List[int] , it will return same thing: List