Voglio ottenere il tipo di una variabile in fase di esecuzione


Risposte:


132

Quindi, in senso stretto, il "tipo di variabile" è sempre presente e può essere passato come parametro di tipo. Per esempio:

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

Ma a seconda di cosa vuoi fare , questo non ti aiuterà. Ad esempio, potresti voler non sapere qual è il tipo di variabile, ma sapere se il tipo di valore è un tipo specifico, come questo:

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

Qui non importa quale sia il tipo di variabile, Any. Ciò che conta, ciò che viene controllato è il tipo 5, il valore. In effetti, Tè inutile: potresti anche averlo scritto def f(v: Any)invece. Inoltre, questo utilizza uno ClassTago un valore Class, che viene spiegato di seguito, e non può controllare i parametri di tipo di un tipo: puoi controllare se qualcosa è a List[_]( Listdi qualcosa), ma non se è, ad esempio, a List[Int]o List[String].

Un'altra possibilità è che tu voglia reificare il tipo di variabile. Cioè, vuoi convertire il tipo in un valore, in modo da poterlo memorizzare, passarlo, ecc. Ciò implica la riflessione e utilizzerai o ClassTago un file TypeTag. Per esempio:

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 ClassTagti consente anche di utilizzare i parametri di tipo ricevuti match. Questo non funzionerà:

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

Ma questo:

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)

Qui sto usando la sintassi dei limiti di contestoB : ClassTag , che funziona proprio come il parametro implicito nell'esempio precedente ClassTag, ma utilizza una variabile anonima.

Si può anche ottenere un ClassTagda un valore Class, come questo:

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è limitato in quanto copre solo la classe base, ma non i suoi parametri di tipo. Cioè, il ClassTagper List[Int]e List[String]è lo stesso List,. Se hai bisogno di parametri di tipo, devi usare TypeTaginvece un file . A TypeTag, tuttavia, non può essere ottenuto da un valore, né può essere utilizzato su un pattern match, a causa della cancellazione di JVM .

Gli esempi con TypeTagpossono diventare piuttosto complessi - nemmeno confrontare due tag di tipo non è esattamente semplice, come si può vedere di seguito:

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

Ovviamente, ci sono modi per rendere vero questo confronto, ma richiederebbe alcuni capitoli di libro per coprire davvero TypeTag, quindi mi fermo qui.

Infine, forse non ti interessa affatto il tipo di variabile. Forse vuoi solo sapere qual è la classe di un valore, nel qual caso la risposta è piuttosto semplice:

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

Sarebbe meglio, tuttavia, essere più specifico su ciò che si desidera ottenere, in modo che la risposta possa essere più pertinente.


Il codice di esempio che hai scritto dopo "Ma questo:" crea confusione. Si compila ma il risultato non è quello che mostri nei commenti. Entrambe le chiamate restituiscono lo stesso risultato: "A è un B". Perché il valore 5è sia un'istanza di Intche un'istanza di Any. A parte questo, la tua spiegazione era perfetta :)
Readren

@Readren Il valore non viene testato, la classe lo è. Intè Any, ma Anynon lo è Int. Funziona su Scala 2.10 e dovrebbe funzionare su Scala 2.11, e non so perché non lo sia.
Daniel C. Sobral

1
Mi spaventa contraddire un'eminenza come te, ma il codice a match { case _: B => ...verifica il tipo del valore effettivo della variabile a, non il tipo della variabile a. Hai ragione in quanto restituisce ciò che dici in scala 2.10.6. Ma dovrebbe essere un bug. In scala 2.11.8 viene testato il tipo del valore effettivo, come dovrebbe.
Readren

Copertura molto bella sulle differenze tra ClassTag e TypeTag, proprio quello che stavo cercando.
marcin_koss

C'è un modo per annullare il controllo?
ChiMo

53

Penso che la domanda sia incompleta. se intendevi che desideri ottenere le informazioni sul tipo di qualche tipo di classe, allora di seguito:

Se desideri stampare come da te specificato, allora:

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 sei in modalità di sostituzione, allora

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

O se desideri solo sapere quale tipo di classe, come spiega @monkjack, "string".getClasspotrebbe risolvere lo scopo


3
per i lettori: questa è la soluzione più utile . Come in Javascript typeof x, qui manOf(x)dì il tipo di dati!
Peter Krauss

23

Se per tipo di variabile si intende la classe di runtime dell'oggetto a cui punta la variabile, è possibile ottenerla tramite il riferimento di classe che hanno tutti gli oggetti.

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

Se tuttavia intendi il tipo con cui è stata dichiarata la variabile, non puoi ottenerlo. Ad esempio, se dici

val name: Object = "sam"

quindi riceverai comunque un Stringrimborso dal codice sopra.


8
Puoi anche farlo name.getClass.getSimpleNameper un output più leggibile
David Arenburg

21

L'ho provato e ha funzionato

val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.