web-dev-qa-db-fra.com

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

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

90
アレックス

Donc, à proprement parler, le "type d'une variable" est toujours présent et peut être transmis comme 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:

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

Ici, peu importe le type de la variable, Any. Ce qui compte, c’est le type de 5, la valeur. En fait, T est inutile - vous auriez aussi bien pu l'écrire def f(v: Any). De plus, ceci utilise soit ClassTag ou une valeur de Class d'une valeur, comme expliqué 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[_] (List de quelque chose), mais pas si c'est, par exemple, un List[Int] ou List[String].

Une autre possibilité est que vous souhaitiez reify 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 soit ClassTag, soit 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"

Un ClassTag vous permettra également d'utiliser les paramètres de type que vous avez reçus sur 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 context Bound, B : ClassTag, qui fonctionne comme le paramètre implicite de l'exemple précédent ClassTag, mais utilise une variable anonyme.

On peut aussi obtenir un ClassTag à partir de Class d'une valeur, 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

Une ClassTag est limitée en ce sens qu'elle ne couvre que la classe de base, mais pas ses paramètres de type. C'est-à-dire que ClassTag pour List[Int] et List[String] est identique, List. Si vous avez besoin de paramètres de type, vous devez utiliser plutôt un TypeTag. Un TypeTag ne peut toutefois pas être obtenu à partir d'une valeur, ni utilisé sur une correspondance de modèle, en raison de la JVM erasure.

Les exemples avec TypeTag peuvent devenir assez complexes - même comparer deux balises de type n'est pas tout à fait 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 couvrir réellement TypeTag, aussi je vais en rester là.

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

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

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

121
Daniel C. Sobral

Je pense que la question est incomplète. si vous voulez dire que vous souhaitez obtenir les informations de type d'une classe de type, 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 connaître le type de classe, comme l'explique @monkjack, "string".getClass pourrait résoudre le problème.

42
Jatin

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

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

Si vous entendez cependant le type de déclaration de la variable, vous ne pouvez pas l'obtenir. Par exemple, si vous dites

val name: Object = "sam"

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

17
monkjack

j'ai testé cela et cela a fonctionné

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