Chcę uzyskać typ zmiennej w czasie wykonywania. Jak mam to zrobic?
źródło
Chcę uzyskać typ zmiennej w czasie wykonywania. Jak mam to zrobic?
Tak więc, ściśle mówiąc, „typ zmiennej” jest zawsze obecny i może być przekazywany jako parametr typu. Na przykład:
val x = 5
def f[T](v: T) = v
f(x) // T is Int, the type of x
Ale w zależności od tego, co chcesz zrobić , to ci nie pomoże. Na przykład może chcieć nie wiedzieć, jaki jest typ zmiennej, ale wiedzieć, czy typ wartości jest określonym typem, na przykład:
val x: Any = 5
def f[T](v: T) = v match {
case _: Int => "Int"
case _: String => "String"
case _ => "Unknown"
}
f(x)
Tu nie ma znaczenia, jaki jest typ zmiennej Any
. Liczy się, co się sprawdza, to rodzaj 5
, wartość. W rzeczywistości T
jest bezużyteczny - równie dobrze mógłbyś go napisać def f(v: Any)
. Ponadto używa to ClassTag
albo wartości Class
, które są wyjaśnione poniżej, i nie można sprawdzić parametrów typu: możesz sprawdzić, czy coś jest List[_]
( List
czegoś), ale nie, czy jest to na przykład a List[Int]
lub List[String]
.
Inną możliwością jest to, że chcesz zmienić typ zmiennej. Oznacza to, że chcesz przekonwertować typ na wartość, aby móc go przechowywać, przekazywać itd. Wymaga to odbicia, a będziesz używać albo ClassTag
lub a TypeTag
. Na przykład:
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 ClassTag
pozwoli ci również użyć parametrów typu, które otrzymałeś match
. To nie zadziała:
def f[A, B](a: A, b: B) = a match {
case _: B => "A is a B"
case _ => "A is not a B"
}
Ale to będzie:
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)
Tutaj używam składni granic kontekstuB : ClassTag
, która działa tak samo jak niejawny parametr w poprzednim ClassTag
przykładzie, ale używa anonimowej zmiennej.
Można również uzyskać a ClassTag
z wartości Class
, na przykład:
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
jest ograniczony, ponieważ obejmuje tylko klasę bazową, ale nie obejmuje jej parametrów typu. Oznacza to, że ClassTag
dla List[Int]
i List[String]
jest taka sama List
. Jeśli potrzebujesz parametrów typu, musisz TypeTag
zamiast tego użyć . TypeTag
Jednak nie można uzyskać z wartości, ani nie może być stosowany na meczu wzór, ze względu na JVM za skasowaniem .
Przykłady z TypeTag
mogą być dość skomplikowane - nawet porównanie dwóch typów tagów nie jest do końca proste, jak widać poniżej:
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
Oczywiście istnieją sposoby, aby to porównanie powróciło, ale naprawdę wymagałoby to kilku rozdziałów w książce TypeTag
, więc na tym zakończę.
Wreszcie, być może w ogóle nie obchodzi cię typ zmiennej. Może po prostu chcesz wiedzieć, jaka jest klasa wartości, w takim przypadku odpowiedź jest raczej prosta:
val x = 5
x.getClass // int -- technically, an Int cannot be a class, but Scala fakes it
Lepiej byłoby jednak bardziej szczegółowo określić, co chcesz osiągnąć, aby odpowiedź była bardziej konkretna.
5
jest zarówno wystąpieniem, jakInt
i wystąpieniemAny
. Poza tym twoje wyjaśnienie było idealne :)Int
jestAny
, aleAny
nie jestInt
. Działa na Scali 2.10 i powinno działać na Scali 2.11 i nie wiem, dlaczego tak nie jest.a match { case _: B => ...
testuje typ rzeczywistej wartości zmienneja
, a nie typ zmienneja
. Masz rację, ponieważ zwraca to, co mówisz w scali 2.10.6. Ale to powinien być błąd. W scali 2.11.8 testowany jest typ wartości rzeczywistej, tak jak powinien.Myślę, że pytanie jest niepełne. jeśli chodziło Ci o to, że chcesz uzyskać informacje o typie jakiejś typeklasy, to poniżej:
Jeśli chcesz drukować zgodnie z ustaleniami:
Jeśli jesteś w trybie repl, to
Lub jeśli chcesz tylko wiedzieć, jaki typ klasy, to, jak wyjaśnia @monkjack,
"string".getClass
może rozwiązać celźródło
typeof x
, podaj tutajmanOf(x)
typ danych!Jeśli przez typ zmiennej masz na myśli klasę środowiska wykonawczego obiektu, na który wskazuje zmienna, możesz to uzyskać za pomocą odwołania do klasy, które mają wszystkie obiekty.
Jeśli jednak masz na myśli typ, jako że zmienna została zadeklarowana, nie możesz tego uzyskać. Np. Jeśli powiesz
wtedy nadal otrzymasz
String
zwrot z powyższego kodu.źródło
name.getClass.getSimpleName
dla bardziej czytelnego wyjściaPrzetestowałem to i zadziałało
źródło