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


Ответы:


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


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возврат из приведенного выше кода.


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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.