Scala では、List[Int] をインスタンス化する場合、インスタンスが List であることは検証できますが、そのインスタンスの個々の要素が Int であることは検証できますが、List[Int] であることは簡単に検証できないという悲しい事実があります。
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!
-unchecked オプションは、型の消去に責任を全面的に押し付けます。
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
case l : List[String] => println("A list of strings?!")
^
A list of strings?!
それはなぜですか? また、どうすれば回避できますか?
ベストアンサー1
この回答
Manifest
では、Scala 2.10 以降では非推奨となっている -API を使用しています。最新の解決策については、以下の回答を参照してください。
Scala は、Java とは異なり、Java 仮想マシン (JVM) がジェネリックを持たないため、型消去を使用して定義されました。つまり、実行時にはクラスのみが存在し、その型パラメータは存在しません。例では、JVM は を処理していることを認識していますscala.collection.immutable.List
が、このリストが でパラメータ化されていることは認識していませんInt
。
幸いなことに、Scala にはそれを回避できる機能があります。それは Manifest です。Manifestは、インスタンスが型を表すオブジェクトであるクラスです。これらのインスタンスはオブジェクトなので、渡したり、保存したり、一般的にメソッドを呼び出したりすることができます。暗黙のパラメータのサポートにより、非常に強力なツールになります。たとえば、次の例を見てみましょう。
object Registry {
import scala.reflect.Manifest
private var map= Map.empty[Any,(Manifest[_], Any)]
def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
map = map.updated(name, m -> item)
}
def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
map get key flatMap {
case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
}
}
}
scala> Registry.register("a", List(1,2,3))
scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))
scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None
要素を保存するときに、その「マニフェスト」も保存します。マニフェストは、インスタンスが Scala 型を表すクラスです。これらのオブジェクトには JVM よりも多くの情報が含まれており、完全なパラメータ化された型をテストできます。
ただし、 はManifest
まだ進化中の機能であることに注意してください。 制限の一例として、現時点では、 は変動性について何も知らず、すべてが共変であると想定しています。 現在開発中の Scala リフレクション ライブラリが完成すれば、 はより安定して堅牢になると思います。