Ответ 1
Итак, строго говоря, "тип переменной" всегда присутствует и может быть передан как параметр типа. Например:
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
для List[Int]
и List[String]
- то же самое, List
. Если вам нужны параметры типа, то вместо этого вы должны использовать TypeTag
. Однако A 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
Было бы лучше, однако, быть более конкретным о том, чего вы хотите достичь, чтобы ответ мог быть более точным.