case文で型パラメータをつかうとき

Scala でこんなプログラムを書いたとします。

tuple match{
  case t:(Int,String) => println("(Int,String) tuple")
  case _ =>  println("Not a (Int,String) tuple!")
}

これ、コンパイル通ります。ただし、例えば、(String,String)のtuple を渡した場合、2行めのt:(Int,String)にマッチしてしまいます。というのは、バイトコードに変換されてしまうと、tの2要素のタプルであることは判断できるものの、tの各要素の型まではわからなくなってしまうからです。

もう少し詳しくみると、型指定の

 (Int,String)

の部分は、

Tuple2[Int,String] 

というように、Tuple2に型パラメータを指定しているのと同じことですが、この型パラメータの[Int,String]の情報がバイトコードになるとごっそり抜け落ちます。Scalaの型パラメータはJavaジェネリクスに変換されるのですが、Javaバイトコードの下位互換性を保つためにバイトコード生成時にジェネリクス情報をすべて消し込むerasureモデルを採用するからです。だから、tはTuple2かどうかはわかるけど、Tuple2[Int,String]かはわからない。

同じ理由で以下のコードも「Stringの」Listかどうかは、みてません(みれないから)

list match{
   case l:List[String] => "This is String List!"
   case _ => "This is not a String List!!"
}

なんでこんなことが気になったかというと、このようなコードがScalaDoc生成時にwarningをいっぱいだしたから。warningを出さない方法は、2種類あります。まず、「「Stringの」Listかどうかなんてそんなに気にしないよ。」という場合。このような場合は、

list match{
  case l:List[_] => "This is a List!"
  case _ => "This is not a List!"
}

と、型パラメータの部分を _ にすればよいです。caseのあとの処理のブロックでListの要素に対してString型のメソッドを実行したい場合も、asInstanceOf を利用してキャストすればいけます。(なんとなくちょっと不細工ですが)

問題は、「Stringの」Listだ、ということを検証したい場合です。以下のページでも議論されていますが、ちょっと工夫しないと難しそうです。

http://stackoverflow.com/questions/1094173/how-do-i-get-around-type-erasure-on-scala-or-why-cant-i-get-the-type-parameter

Listの場合は、

  1. Array にする (Javaの配列はジェネリクスではないから、要素の型の評価ができる)
  2. StringのList をあらわす StringListクラスを作成する

などなどの方法が考えられます。Tupleなどは、2番目の「クラスを作る」という方法をとった方がコードがすっきりしそうな気がします。

今回は文脈上問題なかったので、List[_]の方法を採用しました。いろいろ勉強になります。