web-dev-qa-db-ja.com

実行時に変数の型を取得したい

実行時に変数の型を取得したい。どうすればいいですか?

87
アレックス

したがって、厳密に言えば、「変数の型」は常に存在し、型パラメーターとして渡すことができます。例えば:

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

しかし、あなたが何をしたいのかによってはdo、それはあなたを助けませんたとえば、変数の型が何であるかを知りたくないが、valueの型が次のような特定の型であるかどうかを知りたい場合があります。この:

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]

別の可能性は、変数の型をreifyしたいということです。つまり、型を値に変換したいので、格納したり、渡したりすることができます。これにはリフレクションが含まれ、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"

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)

ここでは、context bounds構文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

ClassTagは、基本クラスのみを対象とし、型パラメーターは対象としないという点で制限されます。つまり、List[Int]List[String]ClassTagは同じListです。型パラメーターが必要な場合は、代わりにTypeTagを使用する必要があります。ただし、JVMのerasureにより、TypeTagは値から取得できず、パターンマッチで使用することもできません。

TypeTagを使用した例は非常に複雑になる可能性があります。2つのタイプタグを比較することさえ、以下に示すように正確ではありません。

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

もちろん、その比較をtrueに戻す方法はありますが、実際にTypeTagをカバーするには本の章がいくつか必要になるので、ここで停止します。

最後に、変数の型はまったく気にしないかもしれません。値のクラスとは何かを知りたいだけかもしれません。その場合、答えはかなり単純です。

val x = 5
x.getClass // int -- technically, an Int cannot be a class, but Scala fakes it

ただし、達成したいことをより具体的にして、答えがより重要になるようにした方が良いでしょう。

119

質問は不完全だと思います。何らかのタイプクラスのタイプ情報を取得したい場合は、以下をご覧ください。

指定したとおりに印刷する場合:

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が説明するように目的を解決するかもしれません

41
Jatin

変数のタイプで、変数が指すオブジェクトの実行時クラスを意味する場合、すべてのオブジェクトが持つクラス参照を通じて取得できます。

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

ただし、変数が宣言された型を意味する場合、それを取得することはできません。例えば、あなたが言うなら

val name: Object = "sam"

上記のコードからStringが返されます。

17
monkjack

私はそれをテストしましたが、うまくいきました

val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
12