はじめに
概要
Scalaに不慣れな筆者が、JavaとScalaのListをごちゃ混ぜにしたところハマりました。普段Javaを書いていてScalaは初心者だよっていう人はハマりやすいかもと思ったので、原因と解決策をシェアします。
環境
- Scala2.12.6
- Java10.0.2
やりたいこと
Twitter4JのAPIで返ってくるツイートのリスト(ResponseList[Status])から必要な情報だけを抜き出して、自作クラスTweet型のListを取得したい。
JavaだとStreamAPIを使えば瞬殺できそう。
元のコード
コード
Twitter4JのAPIから返却されるResponseListはStreamAPIを使えるので、Java風に書くとこんな感じになりました。
※動きません。
val statuses: ResponseList[Status] = twitter.getUserTimeline(paging)
val tweets: List[Tweet] = statuses
.stream()
.map(status => new Tweet(status.getId, status.getText, status.getUser.getName))
.collect(Collectors.toList) //なぜかここでコンパイルエラーが…
原因
StreamAPIの終端操作.collect(Collectors.toList)
で戻るのはjava.util.Listなのに、格納する変数の型がScalaのList型になっていること。
ScalaではScalaのAPIもJavaのAPIも使えますが、両者は別物。同じ名前の型を両者ともに提供していますが、ごちゃまぜに使ってはいけません。
書き直す方針
1.仕方がないのでjava.util.Listを変数の型に採用する
一番楽。
val statuses: ResponseList[Status] = twitter.getUserTimeline(paging)
val tweets: java.util.List[Tweet] = statuses
.stream()
.map(status => new Tweet(status.getId, status.getText, status.getUser.getName))
.collect(Collectors.toList)
だけど、メソッドで変数tweetsを戻すなら、ScalaのListが良い!だってScalaだもの。
2.JavaのコレクションクラスをScalaのコレクションクラスに変換
Scalaのドキュメントによると、ScalaとJavaのコレクションは相互に変換できる模様。
//JavaConvertersクラスがコレクションの変換を担う
import scala.collection.JavaConverters._
val statuses: ResponseList[Status] = twitter.getUserTimeline(paging)
val tweets: List[Tweet] = statuses
//ResponseList(Java) => mutable.Buffer(Scala)
.asScala
//あとはScalaコレクション操作APIを使用
.map(status => new Tweet(status.getId, status.getText, status.getUser.getName))
.toList
JavaConvertersについて補足
ScalaのコレクションとJavaのコレクションの対照表
Scala | Java |
---|---|
Iterator | java.util.Iterator |
Iterator | java.util.Enumeration |
Iterable | java.lang.Iterable |
Iterable | java.util.Collection |
mutable.Buffer | java.util.List |
mutable.Set | java.util.Set |
mutable.Map | java.util.Map |
mutable.ConcurrentMap | java.util.concurrent.ConcurrentMap |
使い方
import collection.JavaConverters._
と宣言した上で、asJava
メソッドやasScala
メソッドを使うことで、変換をすることが可能。ただ、万能というわけでもないらしいので気をつけて使ってください。詳しくは公式ドキュメント