S-JIS[2010-12-30/2013-06-08] 変更履歴

Scala ClassManifest

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

Scala2.10でClassManifestは非推奨になった。[2013-06-08]
ClassTagを使用する。


ClassManifest

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

しかしScalaにはClassManifest(Manifest)というトレイト(とオブジェクト)があり、これで型パラメーターの情報も受け渡せる。
(ClassManifestとManifestの違いはよく分からないが(苦笑)、Scaladocで公開されているのはClassManifestだから、ClassManifestを使えばいいのかな…→ClassManifestとManifestの違い

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

Scala Java類似 備考
scala> classManifest[Int]
res3: ClassManifest[Int] = Int

scala> classManifest[Array[Int]]
res4: ClassManifest[Array[Int]] = Array[Int]
scala> manifest[Int]
res6: Manifest[Int] = Int
  ClassManifestは、Predefで定義されているclassManifest関数を使うと取得できる。
同様に、Manifestはmanifest関数で取得できる。
def func[T](list:List[T], c:ClassManifest[T]) = {
  println(list)
  println(c)
}
scala> val list = List[String]()
list: List[String] = List()

scala> func(list, classManifest[String])
List()
java.lang.String
<T> void func(List<T> list, Class<T> c) {
  System.out.println(list);
  System.out.println(c);
}
List<String> list = new ArrayList<String>();


func(list, String.class);
まず、第2引数にClassManifestを明示的に渡す例。
def func[T](list:List[T])(implicit c:ClassManifest[T]) = {
  println(list)
  println(c)
}
scala> func(list)
List()
java.lang.String
  ClassManifestを暗黙の引数にすれば、
その値を明示的に指定する必要が無くなる。
def func[T:ClassManifest](list:List[T]) = {
  val c = implicitly[ClassManifest[T]]
  println(list)
  println(c)
}
scala> def func[T:ClassManifest](list:List[T]) = 〜
func: [T](list: List[T])(implicit evidence$1: ClassManifest[T])Unit

scala> func(list)
List()
java.lang.String
  Scala2.8で導入されたcontext boundの構文を使った例。
context boundを使ったメソッド定義の結果は、普通の暗黙の引数を使ったのと同じ形になっている。
ただし変数名は自動生成されるので直接は使えない。
そこでimplicitly関数を使ってClassManifestインスタンスを取得する。

ClassManifestとClassとの変換

ClassManifestを使ってjava.lang.Classを取得したりインスタンスを生成したりする方法。[2011-01-04]

  備考
Classの取得
class Example[T](implicit m:ClassManifest[T]) {
  def printClass(): Unit = {
    val c = m.erasure
    println(c)
  }
}
val s = new Example[String]
s.printClass()
 
インスタンスの生成
class Example[T](implicit m:ClassManifest[T]) {
  def create(): T = {
    m.erasure.newInstance().asInstanceOf[T]
  }
}
val s = new Example[String]
val v = s.create()
 
配列の生成
class Example[T](implicit m:ClassManifest[T]) {
  def create(size:Int): Array[T] = {
    m.newArray(size)
  }
}
val s = new Example[Int]
val a = s.create(10)
配列の生成はClassManifestに専用のメソッドがある。
クラス判断
class Example[T](implicit m:ClassManifest[T]) {
  def check(o:AnyRef): Boolean = {
    (o ne null) && m.erasure.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:ClassManifest[T]) {
  def check[S](o:S)(implicit s:ClassManifest[S]): Boolean = {
    m.erasure.isAssignableFrom(s.erasure)
  }
}
Javaのinstanceof相当。

ClassManifestとManifestの違い

ClassManifestとManifestをどう使い分けるのかよく分からないが、当然違いはあるらしい。
参考: What's the difference between ClassManifest and Manifest?

scala> class Foo[T]
defined class Foo

scala> classManifest[Foo[_]]
res0: scala.reflect.ClassManifest[Foo[_]] = Foo[<?>]

scala> manifest[Foo[_]]
res1: scala.reflect.Manifest[Foo[_]] = Foo[_ <: Any]

scala> type T = Foo[_]
defined type alias T

scala> classManifest[T]
res2: ClassManifest[T] = Foo[<?>]

scala> manifest[T]
<console>:8: error: overloaded method value classType with alternatives:
  (prefix: scala.reflect.Manifest[_],clazz: Class[_],args: scala.reflect.Manifest[_]*)scala.reflect.Manifest[T] <and>
  (clazz: Class[T],arg1: scala.reflect.Manifest[_],args: scala.reflect.Manifest[_]*)scala.reflect.Manifest[T] <and>
  (clazz: Class[_])scala.reflect.Manifest[T]
 cannot be applied to (java.lang.Class[Foo[_$1]], scala.reflect.Manifest[_$1])
       manifest[T]
               ^

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