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

アレッ*_*ックス 92 reflection scala

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

Dan*_*ral 126

因此,严格来说,"变量的类型"始终存在,并且可以作为类型参数传递.例如:

val x = 5
def f[T](v: T) = v
f(x) // T is Int, the type of x
Run Code Online (Sandbox Code Playgroud)

但取决于你想做什么,这对没有帮助.例如,可能想知道变量的类型是什么,但要知道的类型是否是某种特定类型,例如:

val x: Any = 5
def f[T](v: T) = v match {
  case _: Int    => "Int"
  case _: String => "String"
  case _         => "Unknown"
}
f(x)
Run Code Online (Sandbox Code Playgroud)

这里变量的类型无关紧要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"
Run Code Online (Sandbox Code Playgroud)

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"
}
Run Code Online (Sandbox Code Playgroud)

但这会:

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)
Run Code Online (Sandbox Code Playgroud)

这里我使用的是上下文边界语法,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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

当然,有一些方法可以让这种比较恢复正确,但是需要一些书章才能真正涵盖TypeTag,所以我会停在这里.

最后,也许你根本不关心变量的类型.也许你只想知道一个值的类是什么,在这种情况下答案很简单:

val x = 5
x.getClass // int -- technically, an Int cannot be a class, but Scala fakes it
Run Code Online (Sandbox Code Playgroud)

然而,更好地了解你想要完成的事情,以便答案可以更加重要.


Jat*_*tin 46

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

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

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]
Run Code Online (Sandbox Code Playgroud)

如果你处于repl模式那么

scala> :type List(1,2,3)
List[Int]
Run Code Online (Sandbox Code Playgroud)

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

  • 对于读者:这是**最有用的解决方案**。与 Javascript 中的“typeof x”一样,这里的“manOf(x)”表示数据类型! (3认同)

mon*_*ack 20

如果通过变量类型表示变量指向的对象的运行时类,则可以通过所有对象具有的类引用来获取此变量.

val name = "sam";
name: java.lang.String = sam
name.getClass
res0: java.lang.Class[_] = class java.lang.String
Run Code Online (Sandbox Code Playgroud)

但是,如果你的意思是变量被声明为的类型,那么你就无法得到它.例如,如果你说

val name: Object = "sam"
Run Code Online (Sandbox Code Playgroud)

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

  • 您还可以执行`name.getClass.getSimpleName`以获得更易读的输出 (4认同)

小智 17

我测试了它并且它有效

val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
Run Code Online (Sandbox Code Playgroud)


小智 6

instance.getClass
Run Code Online (Sandbox Code Playgroud)

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

instance.getClass.getSimpleName
Run Code Online (Sandbox Code Playgroud)

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