Я хочу получить тип переменной во время выполнения. Как мне это сделать?
Ответы:
Итак, строго говоря, «тип переменной» присутствует всегда и может передаваться как параметр типа. Например:
val x = 5
def f[T](v: T) = v
f(x) // T is Int, the type of x
Но в зависимости от того, что вы хотите сделать , это вам не поможет. Например, вы можете захотеть не знать, что это за тип переменной, но знать, является ли тип значения некоторым конкретным типом, например следующим:
val x: Any = 5
def f[T](v: T) = v match {
case _: Int => "Int"
case _: String => "String"
case _ => "Unknown"
}
f(x)
Здесь не имеет значения, какой тип переменной Any
. Важно то, что проверяется, это тип 5
, значение. Фактически, T
это бесполезно - вы могли бы написать его def f(v: Any)
вместо этого. Кроме того, здесь используются значения или ClassTag
или значения Class
, которые объясняются ниже, и невозможно проверить параметры типа типа: вы можете проверить, является ли что-то List[_]
( List
чего-то), но не является ли это, например, List[Int]
или List[String]
.
Другая возможность состоит в том, что вы хотите конкретизировать тип переменной. То есть вы хотите преобразовать тип в значение, чтобы вы могли сохранить его, передать и т. Д. Это включает отражение, и вы будете использовать либо файл, ClassTag
либо файл TypeTag
. Например:
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
также позволит вам использовать параметры типа, которые вы получили match
. Это не сработает:
def f[A, B](a: A, b: B) = a match {
case _: B => "A is a B"
case _ => "A is not a B"
}
Но это будет:
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)
Здесь я использую синтаксис контекстных границB : ClassTag
, который работает так же, как неявный параметр в предыдущем ClassTag
примере, но использует анонимную переменную.
Также можно получить ClassTag
из значения Class
, например:
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
ограничен тем, что он охватывает только базовый класс, но не его параметры типа. То есть ClassTag
for List[Int]
и List[String]
то же самое List
,. Если вам нужны параметры типа, вы должны использовать TypeTag
вместо них. АTypeTag
Однако нельзя получить из значения и нельзя использовать для сопоставления с образцом из-за стирания JVM .
Примеры с TypeTag
могут быть довольно сложными - даже не сравнить два тега типа не совсем просто, как видно ниже:
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
Конечно, есть способы сделать это сравнение верным, но для этого потребуется несколько глав книги TypeTag
, поэтому я остановлюсь на этом.
Наконец, возможно, вам вообще не важен тип переменной. Возможно, вы просто хотите знать, каков класс значения, и в этом случае ответ довольно прост:
val x = 5
x.getClass // int -- technically, an Int cannot be a class, but Scala fakes it
Однако было бы лучше уточнить, чего вы хотите достичь, чтобы ответ был более конкретным.
Int
есть Any
, но Any
нет Int
. Он работает на Scala 2.10 и должен работать на Scala 2.11, и я не знаю, почему это не так.
a match { case _: B => ...
проверяет тип фактического значения переменной a
, а не тип переменной a
. Вы правы в том, что он возвращает то, что вы говорите в scala 2.10.6. Но это должна быть ошибка. В scala 2.11.8 тип фактического значения тестируется, как и должно.
Думаю, вопрос неполный. если вы имели в виду, что хотите получить информацию о типе некоторого класса типов, то ниже:
Если вы хотите печатать, как указали, тогда:
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]
Если вы находитесь в режиме ответа, тогда
scala> :type List(1,2,3)
List[Int]
Или, если вы просто хотите знать, какой тип класса, как объясняет @monkjack, "string".getClass
может решить цель
typeof x
, здесь manOf(x)
указывается тип данных!
Если под типом переменной вы имеете в виду класс среды выполнения объекта, на который указывает переменная, то вы можете получить это через ссылку на класс, которая есть у всех объектов.
val name = "sam";
name: java.lang.String = sam
name.getClass
res0: java.lang.Class[_] = class java.lang.String
Однако если вы имеете в виду тип, в котором была объявлена переменная, вы не можете этого понять. Например, если вы скажете
val name: Object = "sam"
тогда вы все равно получите String
возврат из приведенного выше кода.
name.getClass.getSimpleName
для более читаемого вывода
я проверил это, и это сработало
val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
5
является экземпляромInt
и экземпляромAny
. Кроме того, ваше объяснение было идеальным :)