Quero obter o tipo de variável em tempo de execução

97

Quero obter o tipo de variável em tempo de execução. Como eu faço isso?

ア レ ッ ク ス
fonte

Respostas:

132

Portanto, estritamente falando, o "tipo de variável" está sempre presente e pode ser passado como um parâmetro de tipo. Por exemplo:

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

Mas, dependendo do que você deseja fazer , isso não o ajudará. Por exemplo, pode querer não saber qual é o tipo da variável, mas saber se o tipo do valor é algum 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)

Aqui não importa qual é o tipo da variável Any,. O que importa, o que é verificado é o tipo 5, o valor. Na verdade, Té inútil - você poderia muito bem tê-lo escrito def f(v: Any). Além disso, ele usa um ClassTagou um valor Class, que são explicados abaixo, e não pode verificar os parâmetros de tipo de um tipo: você pode verificar se algo é um List[_]( Listde algo), mas não se é, por exemplo, um List[Int]ou List[String].

Outra possibilidade é que você queira reificar o tipo da variável. Ou seja, você deseja converter o tipo em um valor, para que possa armazená-lo, distribuí-lo etc. Isso envolve reflexão e você usará um ClassTagou um TypeTag. Por exemplo:

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 ClassTagtambém permitirá que você use os parâmetros de tipo recebidos em match. Isso não vai funcionar:

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

Mas isso vai:

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)

Aqui estou usando a sintaxe de limites de contexto,,B : ClassTag que funciona exatamente como o parâmetro implícito no ClassTagexemplo anterior , mas usa uma variável anônima.

Também se pode obter um ClassTagde um valor 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 ClassTagé limitado por cobrir apenas a classe base, mas não seus parâmetros de tipo. Ou seja, o ClassTagpara List[Int]e List[String]é o mesmo List,. Se você precisar de parâmetros de tipo, deverá usar um TypeTag. No TypeTagentanto, A não pode ser obtido de um valor, nem pode ser usado em uma correspondência de padrão, devido ao apagamento da JVM .

Exemplos com TypeTagpodem ser bastante complexos - nem mesmo comparar duas tags de tipo não é exatamente simples, como pode ser visto abaixo:

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

Claro, há maneiras de fazer essa comparação retornar verdadeira, mas seriam necessários alguns capítulos de livro para realmente cobrir TypeTag, então vou parar por aqui.

Finalmente, talvez você não se importe com o tipo de variável. Talvez você apenas queira saber qual é a classe de um valor, caso em que a resposta é bastante simples:

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

Seria melhor, entretanto, ser mais específico sobre o que você deseja realizar, para que a resposta seja mais precisa.

Daniel C. Sobral
fonte
O código de exemplo que você escreveu depois de "Mas isso vai:" é confuso. Ele compila, mas o resultado não é o que você mostra nos comentários. Ambas as chamadas retornam o mesmo resultado: "A é um B". Porque o valor 5é uma instância de Inte uma instância de Any. Tirando isso, sua explicação foi perfeita :)
Readren
@Readren O valor não foi testado, a classe sim. Inté Any, mas Anynão é Int. Funciona no Scala 2.10 e deveria funcionar no Scala 2.11, e não sei por que não.
Daniel C. Sobral
1
Assusta-me contradizer uma eminência como você, mas o código a match { case _: B => ...testa o tipo do valor real da variável a, não o tipo da variável a. Você está certo ao dizer que ele retorna o que você disse em scala 2.10.6. Mas deve ser um bug. No scala 2.11.8, o tipo do valor real é testado, como deveria.
Readren
Uma cobertura muito boa sobre as diferenças entre ClassTag e TypeTag, exatamente o que eu estava procurando.
marcin_koss
Existe uma maneira de verificar isso nulo?
ChiMo
53

Eu acho que a questão está incompleta. se você quis dizer que deseja obter as informações de tipo de alguma typeclass, a seguir:

Se você deseja imprimir conforme especificou:

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]

Se você estiver no modo repl, então

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

Ou se você apenas deseja saber qual é o tipo de classe, então como @monkjack explica "string".getClasspode resolver o propósito

Jatin
fonte
3
para leitores: esta é a solução mais útil . Como em Javascript typeof x, aqui manOf(x)diga o tipo de dados!
Peter Krauss
23

Se por tipo de variável você quer dizer a classe de tempo de execução do objeto para o qual a variável aponta, você pode obter isso por meio da referência de classe que todos os objetos têm.

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

Se você, entretanto, quer dizer o tipo com o qual a variável foi declarada, você não pode obter isso. Por exemplo, se você diz

val name: Object = "sam"

então você ainda terá um Stringretorno do código acima.

sksamuel
fonte
8
Você também pode fazer name.getClass.getSimpleNamepara obter uma saída mais legível
David Arenburg,
21

eu testei isso e funcionou

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