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


回答:


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]

もう1つの可能性は、変数の型を具体化することです。つまり、型を値に変換して、それを格納したり、渡し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は、基本クラスのみをカバーし、型パラメーターはカバーしないという点で制限されています。つまりClassTagList[Int]List[String]は同じですList。型パラメーターが必要な場合は、TypeTag代わりにを使用する必要があります。A TypeTagただし、値から得ることができず、またそれは、パターンマッチに使用することができ、JVMのが原因消去

の例は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

ただし、答えをより的確にとらえることができるように、達成したいことをより具体的にした方がよいでしょう。


「しかし、これは:」の後に書いたコード例は混乱を招くものです。コンパイルはできますが、結果はコメントで示したものとは異なります。どちらの呼び出しも同じ結果を返します:「AはBです」。値5がのインスタンスIntとのインスタンスの両方だからAnyです。それとは別に、あなたの説明は完璧でした:)
Readren

@Readren値はテストされていません。クラスはテストされています。IntですがAny、そうで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 2017年

これをヌルチェックする方法はありますか?
ChiMo 2018

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)はデータ型と言います。
Peter Krauss

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 '19

21

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

val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.