我想在运行时获取变量的类型


Answers:


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]

另一种可能性是要具体化的变量的类型。也就是说,您想要将类型转换为值,以便可以存储它,将它传递给其他对象。这涉及反射,将使用ClassTagTypeTag。例如:

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从的值中获得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

A ClassTag的局限性在于它仅覆盖基类,而不覆盖其类型参数。也就是说,ClassTagfor List[Int]List[String]相同List。如果需要类型参数,则必须使用a TypeTag代替。TypeTag但是,由于JVM的擦除,无法从一个值获得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

但是,最好对要完成的工作进行更具体的说明,以使答案更准确。


您在“但这将:”之后编写的示例代码令人困惑。它可以编译,但结果不是您在注释中显示的结果。两个调用返回相同的结果:“ A是B”。因为值5都是Int又是的实例Any。除此之外,您的解释是完美的:)
阅读

@Readren该值未经测试,该类未经测试。IntAny,但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中,将测试实际值的类型。
Readren

很好地介绍了ClassTag和TypeTag之间的差异,这正是我所寻找的。
marcin_koss

有没有一种方法可以使此检查无效?
ChiMo '18

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]

如果您处于repl模式,则

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为输出提供更好的可读性
David Arenburg

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.