S-JIS[2010-12-30/2013-06-08] 変更履歴
ScalaのリフレクションのClassManifestのメモ。
Scala2.10でClassManifestは非推奨になった。[2013-06-08]
ClassTagを使用する。
|
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を使って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をどう使い分けるのかよく分からないが、当然違いはあるらしい。
参考:
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] ^