104

我想在运行时获取变量的类型。我该怎么做呢?

4

5 回答 5

141

所以,严格来说,“变量的类型”总是存在的,并且可以作为类型参数传递。例如:

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)把它写出来。此外,这使用了ClassTagor 或 value 的Class,这将在下面解释,并且无法检查类型的类型参数:您可以检查某物是否是List[_]List某物的),但不能检查它是否是,例如,aList[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"

AClassTag还将允许您使用收到的类型参数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从值中获取 a 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

AClassTag的限制在于它仅涵盖基类,但不涵盖其类型参数。即ClassTagforList[Int]List[String]是相同的,List。如果需要类型参数,则必须使用 aTypeTag代替。但是,由于 JVM 的擦除TypeTag,不能从值中获取A ,也不能在模式匹配中使用它。

示例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

但是,最好更具体地说明您想要完成的工作,以便答案更中肯。

于 2013-10-15T18:53:06.067 回答
57

我认为这个问题是不完整的。如果你的意思是你想获得一些类型类的类型信息,那么下面:

如果您希望按照指定的方式打印,则:

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]

如果您处于 repl 模式,那么

scala> :type List(1,2,3)
List[Int]

或者,如果您只想知道类类型,那么正如@monkjack 解释的那样,"string".getClass可能会解决这个目的

于 2013-10-15T18:20:27.827 回答
25

如果变量的类型是指变量指向的对象的运行时类,那么您可以通过所有对象具有的类引用来获取它。

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

但是,如果您的意思是变量被声明为的类型,那么您将无法理解。例如,如果你说

val name: Object = "sam"

那么你仍然会String从上面的代码中得到回报。

于 2013-10-15T17:12:36.637 回答
23

我已经测试过了,它有效

val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
于 2017-07-09T17:20:48.963 回答
2
instance.getClass

以上方法返回实例类的路径。要获取实例的确切类名称,请尝试以下操作:

instance.getClass.getSimpleName

这是示例: 在此处输入图像描述

于 2021-05-18T11:10:06.110 回答