Chcę uzyskać typ zmiennej w czasie wykonywania. Jak mam to zrobic?
Odpowiedzi:
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.
Int
jest Any
, ale Any
nie jest Int
. 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 zmiennej a
, a nie typ zmiennej a
. 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:
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]
Jeśli jesteś w trybie repl, to
scala> :type List(1,2,3)
List[Int]
Lub jeśli chcesz tylko wiedzieć, jaki typ klasy, to, jak wyjaśnia @monkjack, "string".getClass
może rozwiązać cel
typeof x
, podaj tutaj manOf(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.
val name = "sam";
name: java.lang.String = sam
name.getClass
res0: java.lang.Class[_] = class java.lang.String
Jeśli jednak masz na myśli typ, jako że zmienna została zadeklarowana, nie możesz tego uzyskać. Np. Jeśli powiesz
val name: Object = "sam"
wtedy nadal otrzymasz String
zwrot z powyższego kodu.
name.getClass.getSimpleName
dla bardziej czytelnego wyjścia
Przetestowałem to i zadziałało
val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
5
jest zarówno wystąpieniem, jakInt
i wystąpieniemAny
. Poza tym twoje wyjaśnienie było idealne :)