Ich möchte den Typ einer Variablen zur Laufzeit erhalten. Wie mache ich das?
quelle
Ich möchte den Typ einer Variablen zur Laufzeit erhalten. Wie mache ich das?
Genau genommen ist der "Typ einer Variablen" immer vorhanden und kann als Typparameter weitergegeben werden. Beispielsweise:
val x = 5
def f[T](v: T) = v
f(x) // T is Int, the type of x
Aber je nachdem, was Sie tun möchten, hilft Ihnen das nicht weiter. Beispielsweise möchten Sie möglicherweise nicht wissen, um welchen Typ es sich bei der Variablen handelt, sondern ob es sich bei dem Typ des Werts um einen bestimmten Typ handelt, z.
val x: Any = 5
def f[T](v: T) = v match {
case _: Int => "Int"
case _: String => "String"
case _ => "Unknown"
}
f(x)
Hier spielt es keine Rolle, was der Typ der Variablen ist Any
. Was zählt, was überprüft wird, ist die Art 5
, der Wert. In der Tat T
ist nutzlos - Sie könnten es genauso gut def f(v: Any)
stattdessen geschrieben haben . Dies verwendet auch entweder ClassTag
oder einen Wert Class
, der unten erläutert wird, und kann die Typparameter eines Typs nicht überprüfen: Sie können überprüfen, ob etwas ein List[_]
( List
von etwas) ist, aber nicht, ob es beispielsweise ein List[Int]
oder ist List[String]
.
Eine andere Möglichkeit besteht darin, dass Sie den Typ der Variablen ändern möchten . Das heißt, Sie möchten den Typ in einen Wert konvertieren, damit Sie ihn speichern, weitergeben usw. Dies beinhaltet Reflexion und Sie verwenden entweder ClassTag
oder a TypeTag
. Beispielsweise:
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"
Mit A ClassTag
können Sie auch Typparameter verwenden, für die Sie empfangen haben match
. Das wird nicht funktionieren:
def f[A, B](a: A, b: B) = a match {
case _: B => "A is a B"
case _ => "A is not a B"
}
Aber das wird:
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)
Hier bin ich mit den Kontext Grenzen Syntax B : ClassTag
, die ebenso wie die impliziten Parameter in dem vorherigen Werk ClassTag
Beispiel, verwendet aber eine anonyme Variable.
Man kann auch ClassTag
einen Wert Class
wie folgt erhalten:
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
ist insofern begrenzt, als es nur die Basisklasse abdeckt, nicht jedoch deren Typparameter. Das heißt, das ClassTag
für List[Int]
und List[String]
ist das gleiche List
. Wenn Sie Typparameter benötigen, müssen Sie TypeTag
stattdessen a verwenden. A TypeTag
kann jedoch aufgrund der Löschung durch JVM weder aus einem Wert erhalten noch für eine Musterübereinstimmung verwendet werden .
Beispiele mit TypeTag
können sehr komplex werden - nicht einmal das Vergleichen von zwei Typ-Tags ist nicht ganz einfach, wie unten zu sehen ist:
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
Natürlich gibt es Möglichkeiten, diesen Vergleich wahr werden zu lassen, aber es würde ein paar Buchkapitel erfordern, um wirklich zu behandeln TypeTag
, also werde ich hier aufhören.
Schließlich interessiert Sie der Typ der Variablen vielleicht überhaupt nicht. Vielleicht möchten Sie nur wissen, was die Klasse eines Wertes ist. In diesem Fall ist die Antwort ziemlich einfach:
val x = 5
x.getClass // int -- technically, an Int cannot be a class, but Scala fakes it
Es wäre jedoch besser, genauer zu sagen, was Sie erreichen möchten, damit die Antwort genauer auf den Punkt kommt.
5
sowohl eine InstanzInt
als auch eine Instanz von istAny
. Abgesehen davon war Ihre Erklärung perfekt :)Int
istAny
, aberAny
nichtInt
. Es funktioniert unter Scala 2.10, und es sollte unter Scala 2.11 funktionieren, und ich weiß nicht, warum es nicht so ist.a match { case _: B => ...
testet den Typ des tatsächlichen Werts der Variablena
, nicht den Typ der Variablena
. Sie haben Recht damit, dass es das zurückgibt, was Sie in Scala 2.10.6 sagen. Aber es sollte ein Fehler sein. In Scala 2.11.8 wird der Typ des Istwerts so getestet, wie er sollte.Ich denke, die Frage ist unvollständig. Wenn Sie gemeint haben, dass Sie die Typinformationen einer Typklasse erhalten möchten, gehen Sie wie folgt vor:
Wenn Sie wie angegeben drucken möchten, gehen Sie wie folgt vor:
Wenn Sie sich dann im Repl-Modus befinden
Oder wenn Sie nur wissen möchten, was der Klassentyp ist
"string".getClass
, kann dies den Zweck lösen, wie @monkjack erklärtquelle
typeof x
, hiermanOf(x)
sagen Sie den Datentyp!Wenn Sie unter dem Typ einer Variablen die Laufzeitklasse des Objekts verstehen, auf das die Variable zeigt, können Sie dies über die Klassenreferenz erhalten, über die alle Objekte verfügen.
Wenn Sie jedoch den Typ meinen, als den die Variable deklariert wurde, können Sie diesen nicht erhalten. ZB wenn du sagst
dann erhalten Sie immer noch eine
String
Rückmeldung vom obigen Code.quelle
name.getClass.getSimpleName
für eine besser lesbare Ausgabe tunIch habe das getestet und es hat funktioniert
quelle