Je veux obtenir le type d'une variable à l'exécution

97

Je veux obtenir le type d'une variable au moment de l'exécution. Comment puis-je faire cela?

ア レ ッ ク ス
la source

Réponses:

132

Donc, à proprement parler, le "type d'une variable" est toujours présent, et peut être passé en paramètre de type. Par exemple:

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

Mais selon ce que vous voulez faire , cela ne vous aidera pas. Par exemple, vous voudrez peut-être ne pas savoir quel est le type de la variable, mais savoir si le type de la valeur est un type spécifique, tel que celui-ci:

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

Ici , il n'a pas d' importance quel est le type de la variable, Any. Ce qui compte, c'est le type de 5, la valeur qui est vérifié . En fait, Tc'est inutile - vous auriez aussi bien pu l'écrire à la def f(v: Any)place. De plus, cela utilise soit ClassTagou une valeur Class, qui sont expliquées ci-dessous, et ne peut pas vérifier les paramètres de type d'un type: vous pouvez vérifier si quelque chose est un List[_]( Listde quelque chose), mais pas s'il s'agit, par exemple, d'un List[Int]ou List[String].

Une autre possibilité est que vous souhaitiez réifier le type de la variable. Autrement dit, vous voulez convertir le type en une valeur, afin de pouvoir le stocker, le transmettre, etc. Cela implique une réflexion, et vous utiliserez l'un ClassTagou l' autre TypeTag. Par exemple:

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 vous ClassTagpermettra également d'utiliser les paramètres de type que vous avez reçus match. Cela ne fonctionnera pas:

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

Mais cela va:

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)

Ici, j'utilise la syntaxe des limites de contexteB : ClassTag , qui fonctionne comme le paramètre implicite de l' ClassTagexemple précédent , mais utilise une variable anonyme.

On peut également obtenir un à ClassTagpartir d'une valeur Class, comme ceci:

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 limité en ce qu'il ne couvre que la classe de base, mais pas ses paramètres de type. Autrement dit, le ClassTagpour List[Int]et List[String]est le même List,. Si vous avez besoin de paramètres de type, vous devez utiliser un à la TypeTagplace. TypeTagCependant, A ne peut pas être obtenu à partir d'une valeur, ni utilisé sur une correspondance de modèle, en raison de l' effacement de la JVM .

Les exemples avec TypeTagpeuvent devenir assez complexes - même pas comparer deux balises de type n'est pas exactement simple, comme on peut le voir ci-dessous:

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

Bien sûr, il existe des moyens de rendre cette comparaison vraie, mais il faudrait quelques chapitres de livre pour vraiment couvrir TypeTag, alors je vais m'arrêter ici.

Enfin, peut-être que vous ne vous souciez pas du tout du type de variable. Peut-être voulez-vous simplement savoir quelle est la classe d'une valeur, auquel cas la réponse est assez simple:

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

Il serait préférable, cependant, d'être plus précis sur ce que vous voulez accomplir, afin que la réponse soit plus précise.

Daniel C. Sobral
la source
L'exemple de code que vous avez écrit après "Mais ce sera:" est déroutant. Il compile mais le résultat n'est pas celui que vous montrez dans les commentaires. Les deux appels renvoient le même résultat: "A est un B". Parce que la valeur 5est à la fois une instance Intet une instance de Any. En dehors de cela, votre explication était parfaite :)
Readren
@Readren La valeur n'est pas testée, la classe l'est. Intest Any, mais Anyn'est pas Int. Cela fonctionne sur Scala 2.10, et cela devrait fonctionner sur Scala 2.11, et je ne sais pas pourquoi ce n'est pas le cas.
Daniel
1
Cela me fait peur de contredire une éminence comme vous, mais le code a match { case _: B => ...teste le type de la valeur réelle de la variable a, pas le type de la variable a. Vous avez raison en ce qu'il renvoie ce que vous dites dans scala 2.10.6. Mais ça devrait être un bug. Dans scala 2.11.8, le type de la valeur réelle est testé, comme il se doit.
Readren le
Très belle couverture sur les différences entre ClassTag et TypeTag, exactement ce que je recherchais.
marcin_koss
Existe-t-il un moyen d'annuler la vérification?
ChiMo du
53

Je pense que la question est incomplète. si vous vouliez dire que vous souhaitez obtenir les informations de type d'une classe de types, alors ci-dessous:

Si vous souhaitez imprimer comme vous l'avez spécifié, alors:

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 vous êtes en mode repl, alors

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

Ou si vous souhaitez simplement savoir quel type de classe, comme l'explique @monkjack, cela "string".getClasspourrait résoudre le problème

Jatin
la source
3
pour les lecteurs: c'est la solution la plus utile . Comme en Javascript typeof x, manOf(x)dites ici le type de données!
Peter Krauss
23

Si par le type d'une variable vous entendez la classe d'exécution de l'objet vers lequel pointe la variable, vous pouvez l'obtenir via la référence de classe que tous les objets ont.

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

Si toutefois vous voulez dire le type sous lequel la variable a été déclarée, vous ne pouvez pas l'obtenir. Par exemple, si vous dites

val name: Object = "sam"

alors vous obtiendrez toujours un Stringretour du code ci-dessus.

sksamuel
la source
8
Vous pouvez également le faire name.getClass.getSimpleNamepour une sortie plus lisible
David Arenburg
21

j'ai testé ça et ça a marché

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