S-JIS[2013-06-08] 変更履歴

Scala ClassTag

ScalaリフレクションのClassTagのメモ。


概要

Javaは型消去(type erasure)という仕組みになっているので、実行時にジェネリクス(型パラメーター)の情報が消えてしまう。
ScalaもJavaVM上で動いているので、事情は同じ。

しかしScala(2.10以降)にはClassTagというトレイト(とオブジェクト)があり、これで型パラメーターの情報も受け渡せる。
(2.9以前はClassManifestを使う)

Javaで型パラメーターが必要なメソッドを作る場合は、引数でClassを渡すようなロジックにする必要があった。
ScalaでもJavaと同様にClassTagを引数で渡す必要がある。しかし暗黙の引数を利用して、明示的に記述しなくても済む。

Scala 備考
import scala.reflect.ClassTag
def f[T](list: Seq[T])(implicit c: ClassTag[T]) = {
  println(list)
  println(c)
}
scala> f(Seq("a", "b", "c"))
List(a, b, c)
java.lang.String
ClassTagを暗黙の引数にすれば、その値を明示的に指定しなくてよい。
def f[T: ClassTag](list: Seq[T]) = {
  val c = implicitly[ClassTag[T]]
  println(list)
  println(c)
}
scala> f(Seq("a", "b", "c"))
List(a, b, c)
java.lang.String
context boundの構文を使った例。
context boundを使ったメソッド定義の結果は、普通の暗黙の引数を使ったのと同じ形になっている。
ただし変数名は自動生成されるので直接は使えない。
そこでimplicitly関数を使ってClassTagインスタンスを取得する。

ClassTagとClassとの変換

ClassTagを使ってjava.lang.Classを取得したりインスタンスを生成したりする方法。

  備考
Classの取得
class Example[T](implicit m: ClassTag[T]) {
  def printClass(): Unit = {
    val c = m.runtimeClass
    println(c)
  }
}
val s = new Example[String]
s.printClass()
runtimeClassでClass[_]が取れる。
インスタンスの生成
class Example[T](implicit m: ClassTag[T]) {
  def create(): T = {
    m.runtimeClass.newInstance().asInstanceOf[T]
  }
}
val s = new Example[String]
val v = s.create()
 
配列の生成
class Example[T](implicit m: ClassTag[T]) {
  def create(size:Int): Array[T] = {
    m.newArray(size)
  }
}
val s = new Example[Int]
val a = s.create(10)
配列の生成はClassTagに専用のメソッドがある。
クラス判断
class Example[T](implicit m: ClassTag[T]) {
  def check(o:AnyRef): Boolean = {
    (o ne null) && m.runtimeClass.isInstance(o)
  }
}
val d = new Example[java.util.Date]
assert( d.check(new java.util.Date()) )
assert( d.check(new java.sql.Date(0)) )
assert(!d.check("abc") )
assert(!d.check(null) )

val q = new Example[java.sql.Date]
assert( q.check(new java.sql.Date(0)) )
assert(!q.check(new java.util.Date()) )
Javaのinstanceof相当。
class Example[T](implicit m: ClassTag[T]) {
  def check[S](o: S)(implicit s: ClassTag[S]): Boolean = {
    m.runtimeClass.isAssignableFrom(s.runtimeClass)
  }
}
Javaのinstanceof相当。

Scala目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま