ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

Scalaコミュのジェネリクスで特定の型を渡してリストを得る方法

  • mixiチェック
  • このエントリーをはてなブックマークに追加
今、ジェネリクスもしくは他の方法を使って、型を指定して、型に一致するリストを得る
エレガントな方法を探しています。

例えば、

case class C
case class D extends C

というクラスがあって、

res31: List[Any] = List(D(aa), D(bb), C(dd), D(ee))

という感じのリストがあるときに、この中から、Dクラスのインスタンスのリストを取得したい
場合、

for(i <- res31 if i.isInstanceOf[D]) yield i

といったように、直接「D」クラスを指定すると、このようにするなり、パターンマッチする
なりして、取得できますが、Dclassを直接指定せず、関数にわたすなり、ジェネリクスで
指定するなりして取得する方法が、いまいち見つかりません。たとえば、

scala> def typeOwnedElements[A](list:List[Any]):List[Any] = {
| for(i <- list if i.isInstanceOf[A]) yield i
| }

という感じの関数を定義して
scala> typeOwnedElements[D](res31)
res32: List[Any] = List(D(aa), D(bb), D(ee))

という結果が欲しい感じなのですが、実際にこの関数を定義すると、

---------------------
scala> def typeOwnedElements[A](list:List[Any]):List[Any] = {
| for(i <- list if i.isInstanceOf[A]) yield i
| }
<console>:5: warning: abstract type A in type is unchecked since it is eliminate
d by erasure
for(i <- list if i.isInstanceOf[A]) yield i
^
typeOwnedElements: [A](List[Any])List[Any]
---------------------
というワーニングがでて、GenericTypeのAが無効化されてしまうっぽいです。だから
こいつで実行すると、、、


res33: List[Any] = List(D(aa), D(bb), C(dd), D(ee))

という感じで型指定が無視されて実行されてしまう様子です。

この方法に全然こだわっていませんが、関数に、型を渡して、その型に該当する
リスト一覧を取得するいい方法ってありませんでしょうか?(特にかっこよければ
なおいいです)

とっても初歩的なことかもしれませんが、結構、上記以外にいろいろ試してはまって
いますのでお知恵がある方のコメントが戴ければ嬉しいです(泣)

コメント(6)

ちりとてちん。まず質問です。
> case class C
> case class D extends C

>res31: List[Any] = List(D(aa), D(bb), C(dd), D(ee))

とありますが、もし型推論がちゃんと働けば、res31: List[C]となると思いますが
なぜList[Any]なんでしょうね。。。


いいやり方は思いつきませんが、とりあえずJavaのreflection機能はScalaでも使えるので、
たとえば、getClass():ClassやgetClass().getName():Stringを使ってみてはいかがでしょうか。
ありがとうございます。

List[Any]なのは、私の編集漏れです。見やすいように結果を編集したのですが、編集漏れでした。
実際の実行結果はList[C]です。

ご指摘の通り結局Javaのリフレクションを使いました。Javaのリフレクションのサポートは口頭で
教えて頂いたとおりScalaの2.7.2からまともにサポートみたいなので、それに代えてから、下記の
ような作りにしました。型決定性がないですがとりあえず目的は果たせそうです。

私もJavaのリフレクションを使って作るサンプルも考えていたのですが、対話式のscalaだと
どうも挙動が変だったので、おかしいなと思っていましたが、Scalaのバージョン問題ではなく、
私の単純ミスだったようです。

ありがとうございました。下記ので、目標としていた、Javaのインターフェイスの実装の有無
みたいなのも判断できそうです。

でも、本当は、かっこよくジェネリクスで決めたかったですが残念(笑)


----------------------
def typeOwnedElements(className:String, list:List[AnyRef]):List[AnyRef]={
for(i <- list if java.lang.Class.forName(className).isInstance(i)) yield i
}
----------------------
package scalasample

case class AA
case class BB extends AA
case class CC extends com.change_vision.jude.api.inf.model.IHyperlinkOwner {
def getHyperlinks = {
null
}
}
object StringSample {
def typeOwnedElements(className:String, list:List[AnyRef]):List[AnyRef]={
for(i <- list if java.lang.Class.forName(className).isInstance(i)) yield i
}

/**
* @param args the command line arguments
*/
def main(args: Array[String]):Unit = {
val list = List(AA(), BB(), AA(), AA())
println(typeOwnedElements("scalasample.AA", list))
println(typeOwnedElements("scalasample.BB", list))
val list2 = List(AA(), BB(), CC(), AA(), BB())
println(typeOwnedElements("com.change_vision.jude.api.inf.model.IHyperlinkOwner", list2))
// println(typeOwnedElements("xxx", list2)) class not found.

}

----------------------実行結果
run:
List(AA(), BB(), AA(), AA())
List(BB())
List(CC())
補足っす。本当は

def typeOwnedElements[A](clazz:java.lang.Class[A], list:List[AnyRef]):List[AnyRef]={
for(i <- list if clazz.isInstance(i)) yield i
}

とか定義して使う方は

typeOwnedElements(com.change_vision.jude.api.inf.model.IHyperlinkOwner.class, list2)

とかで少なくともコンパイル時にチェックできるようにしたかったのですが、メソッドに
関しては上記の指定で作れますが、Scalaではjavaのように、クラス名.clsssでjava.lang.Class
のインスタンスを指定できないみたいっすね…これができればコンパイルチェックぐらいは
かかるのでうれしかったですが・・・。

とりあえずはすっきりしませんが、目的は果たせました。

というわけでご報告まででした。
もう解決してしまったようですが。

私もタイプパラメータで型を特定できたらいいと思う場面によく出会いました。

詳しいことは分かりませんが、JavaVMは実行時に型情報を持っていない、ということでErasureというメカニズム(?)を使っているようです。

なのでパラメータタイプで型を調べるのは基本的に無理なのかな? と思っていました。

もしも解決案が分かったら私も知りたいです。


例えば 簡潔な表現なら以下のようなコードではどうでしょう

 list.filter(_.isInstanceOf[D])  //クラスDを抽出
解決された後のようなので今更の補足ですが、Genericsの型情報はコンパイラによってコンパイル時に
消去されてしまうので、基本的にGenericsだけで解決するのは無理かと思います。Generics
の実装として、C#のようにGenericsの型情報をコンパイル時に消去しない仕組みを採用している
言語ならおそらく可能ですが、Scalaでそのような仕組みを採用するとJava Genericsとの互換性を
捨てることになるでしょうし、難しいところですね。
kanさん

ありがとうございます。

例えば 簡潔な表現なら以下のようなコードではどうでしょう

 list.filter(_.isInstanceOf[D])  //クラスDを抽出

→これはかっこいいですね。特に専用のメソッドを作らなくてもコード量がほぼかわりませんね!

みずさん

 なるほど、そうなんですか。納得しました。ありがとうございます。また別のお願いもするかも
ですが、よろしくです(笑)

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

Scala 更新情報

Scalaのメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング