Quiero obtener el tipo de variable en tiempo de ejecución.

97

Quiero obtener el tipo de variable en tiempo de ejecución. ¿Cómo hago esto?

ア レ ッ ク ス
fuente

Respuestas:

132

Entonces, estrictamente hablando, el "tipo de una variable" siempre está presente y puede pasarse como un parámetro de tipo. Por ejemplo:

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

Pero dependiendo de lo que quieras hacer , eso no te ayudará. Por ejemplo, es posible que no desee saber cuál es el tipo de variable, pero sí saber si el tipo de valor es un tipo específico, como este:

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

Aquí no importa cuál es el tipo de la variable, Any. Lo que importa, lo que se comprueba es el tipo de 5, el valor. De hecho, Tes inútil; bien podría haberlo escrito en su def f(v: Any)lugar. Además, esto usa ClassTago un valor Class, que se explica a continuación, y no puede verificar los parámetros de tipo de un tipo: puede verificar si algo es un List[_]( Listde algo), pero no si es, por ejemplo, un List[Int]o List[String].

Otra posibilidad es que desee cosificar el tipo de variable. Es decir, desea convertir el tipo en un valor, de modo que pueda almacenarlo, pasarlo, etc. Esto implica una reflexión y utilizará ClassTago bien a TypeTag. Por ejemplo:

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 ClassTagtambién le permitirá utilizar los parámetros de tipo que recibió match. Esto no funcionará:

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

Pero esto hará:

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)

Aquí estoy usando la sintaxis de límites de contextoB : ClassTag , que funciona igual que el parámetro implícito en el ClassTagejemplo anterior , pero usa una variable anónima.

También se puede obtener un ClassTagvalor de Class, como este:

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 ClassTagestá limitado porque solo cubre la clase base, pero no sus parámetros de tipo. Es decir, el ClassTagpara List[Int]y List[String]es el mismo List. Si necesita parámetros de tipo, debe usar un TypeTagen su lugar. Sin TypeTagembargo, no se puede obtener a partir de un valor, ni se puede utilizar en una coincidencia de patrón, debido al borrado de JVM .

Los ejemplos con TypeTagpueden volverse bastante complejos; ni siquiera comparar dos etiquetas de tipo no es exactamente simple, como se puede ver a continuación:

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

Por supuesto, hay formas de hacer que esa comparación resulte verdadera, pero requeriría algunos capítulos de libro para cubrir realmente TypeTag, así que me detendré aquí.

Finalmente, tal vez no le importe en absoluto el tipo de variable. Tal vez solo desee saber cuál es la clase de un valor, en cuyo caso la respuesta es bastante simple:

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

Sin embargo, sería mejor ser más específico sobre lo que desea lograr, para que la respuesta sea más precisa.

Daniel C. Sobral
fuente
El código de ejemplo que escribió después de "Pero esto será:" es confuso. Se compila pero el resultado no es el que muestra en los comentarios. Ambas llamadas devuelven el mismo resultado: "A es una B". Porque el valor 5es una instancia de Inty una instancia de Any. Aparte de eso, su explicación fue perfecta :)
Readren
@Readren El valor no se prueba, la clase sí. Intes Any, pero Anyno es Int. Funciona en Scala 2.10 y debería funcionar en Scala 2.11, y no sé por qué no lo es.
Daniel C. Sobral
1
Me da miedo contradecir a una eminencia como tú, pero el código a match { case _: B => ...prueba el tipo del valor real de la variable a, no el tipo de la variable a. Tiene razón en que devuelve lo que dice en scala 2.10.6. Pero debería ser un error. En scala 2.11.8 se prueba el tipo de valor real, como debería.
Readren
Muy buena cobertura sobre las diferencias entre ClassTag y TypeTag, justo lo que estaba buscando.
marcin_koss
¿Hay alguna forma de anular la verificación de esto?
ChiMo
53

Creo que la pregunta está incompleta. si quiso decir que desea obtener la información de tipo de alguna clase de tipo, a continuación:

Si desea imprimir como ha especificado, entonces:

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]

Si está en modo de respuesta,

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

O si solo desea saber cuál es el tipo de clase, como explica @monkjack, "string".getClasspodría resolver el propósito

Jatin
fuente
3
para los lectores: esta es la solución más útil . Como en Javascript typeof x, aquí manOf(x)diga el tipo de datos.
Peter Krauss
23

Si por tipo de variable te refieres a la clase de tiempo de ejecución del objeto al que apunta la variable, entonces puedes obtenerla a través de la referencia de clase que tienen todos los objetos.

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

Sin embargo, si se refiere al tipo con el que se declaró la variable, no puede obtenerlo. Por ejemplo, si dices

val name: Object = "sam"

entonces aún obtendrá una Stringdevolución del código anterior.

sksamuel
fuente
8
También puede hacerlo name.getClass.getSimpleNamepara una salida más legible
David Arenburg
21

lo he probado y funcionó

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