Я хочу получить тип переменной во время выполнения

97

Я хочу получить тип переменной во время выполнения. Как мне это сделать?

ア レ ッ ク ス
источник

Ответы:

132

Итак, строго говоря, «тип переменной» присутствует всегда и может передаваться как параметр типа. Например:

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ограничен тем, что он охватывает только базовый класс, но не его параметры типа. То есть ClassTagfor 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

Однако было бы лучше уточнить, чего вы хотите достичь, чтобы ответ был более конкретным.

Дэниел С. Собрал
источник
Пример кода, который вы написали после «Но это будет:», сбивает с толку. Он компилируется, но результат не тот, который вы показываете в комментариях. Оба вызова возвращают один и тот же результат: «А - это Б». Поскольку значение 5является экземпляром Intи экземпляром Any. Кроме того, ваше объяснение было идеальным :)
Ридрен
@Readren Значение не проверяется, класс проверяется. Intесть Any, но Anyнет Int. Он работает на Scala 2.10 и должен работать на Scala 2.11, и я не знаю, почему это не так.
Daniel C. Sobral
1
Меня пугает противоречие с таким выдающимся лицом, как вы, но код a match { case _: B => ...проверяет тип фактического значения переменной a, а не тип переменной a. Вы правы в том, что он возвращает то, что вы говорите в scala 2.10.6. Но это должна быть ошибка. В scala 2.11.8 тип фактического значения тестируется, как и должно.
Ридрен
Очень хорошее освещение различий между ClassTag и TypeTag, именно то, что я искал.
marcin_koss
Есть ли способ обнулить это?
ChiMo
53

Думаю, вопрос неполный. если вы имели в виду, что хотите получить информацию о типе некоторого класса типов, то ниже:

Если вы хотите печатать, как указали, тогда:

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может решить цель

Jatin
источник
3
для читателей: это самое полезное решение . Как и в Javascript typeof x, здесь manOf(x)указывается тип данных!
Питер Краусс
23

Если под типом переменной вы имеете в виду класс среды выполнения объекта, на который указывает переменная, то вы можете получить это через ссылку на класс, которая есть у всех объектов.

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

Однако если вы имеете в виду тип, в котором была объявлена ​​переменная, вы не можете этого понять. Например, если вы скажете

val name: Object = "sam"

тогда вы все равно получите Stringвозврат из приведенного выше кода.

Sksamuel
источник
8
Вы также можете сделать name.getClass.getSimpleNameдля более читаемого вывода
Дэвид Аренбург
21

я проверил это, и это сработало

val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
СЕНХАДЖИ РАЗИ ХАМА
источник