Scala+Android小ネタ集

(この記事は Scala Advent Calendar jp 2010 の5日目です。)

Scala+Androidで過去にはまったことなどを小ネタとして紹介します。
Androidに限らず、JavaのライブラリをScalaから使用する上でも役にたつかも。

java.lang.Classのインスタンスを取得する

ScalaではclassOfを使って取得できます。
Androidでは明示的Intentを作成するときなどに使います。

// Javaの場合
new android.content.Intent(this, HogeService.class)
// Scalaの場合
new android.content.Intent(this, classOf[HogeService])

scala予約語とおなじ名前のメソッドがあってコンパイルエラーになる

android.content.UriMatcherにはmatchメソッドがあってこれがscala予約語matchとかぶってコンパイルエラーになります。
そういう場合は`(バッククォート)を使ってください。たとえばこんな感じになります。

uriMatcher.`match`(url) match{
    // case ...
}

java.lang.Threadのyieldメソッドとかも同じはずです。

Javaの<?>はScalaでどう書くのか

[_]です。

listview.setOnItemClickListener(
  new OnItemClickListener(){
    override def onItemClick(av:AdapterView[_] , view:View, pos:Int, id:Long){
      // ...
    }
})

Javaクラスのstatic field

Javaクラスを継承したとき親クラスのstatic fieldにはクラス修飾なしではアクセスできません。

Androidの場合だとServiceを継承してonStartCommandでreturn START_STICKYなどと書くとコンパイラに怒られてしまいます。
return Service.START_STICKYと書く必要があります。

これはどういうことなのでしょうか。static fieldはScala的にはコンパニオンオブジェクトに属しているとみなされる、とかそういう理由でしょうか?(テキトウ)

メンバとしてボタンなどの画面要素を保持する

AndroidではonCreateの前に画面要素を取得しようとすると例外を投げられてしまいます。
なのでメンバとして画面要素を保持したい場合はlazy valを使いましょう。

class HogeActivity extends PreferenceActivity {
  private lazy val label = findPreference("label").asInstanceOf[EditTextPreference]
  // ...
}

おまけ android.database.CursorをIterator

android.database.Cursorはそのままだといかにも手続きっぽいのでIteratorにimplicit conversionしてやるとScalaっぽい感じ(?)になります。
IteratorにするにはhasNextとnextさえ定義してやればよいので簡単です。

object AndroidHelper {
  import android.database.Cursor

  private object EmptyCursorIter extends Iterator[Cursor]{
    def hasNext = false
    def next:Cursor = throw new java.util.NoSuchElementException()
  }

  private class CursorIter(cur: Cursor) extends Iterator[Cursor]{
    def hasNext = !cur.isLast()
    def next:Cursor =
      if (cur.moveToNext) cur
      else
	throw new java.util.NoSuchElementException()
  }
    
  implicit def cursorToIterator(cur: Cursor): Iterator[Cursor] = 
    if(!cur.moveToFirst) EmptyCursorIter
    else{
      cur.moveToPrevious
      new CursorIter(cur)
    }
}

これでCursorに対してforeachやmapが使えるようになります。
しかし調子にのってloan patternとか使って

def hogeList = using(getCursor()){ c => c.map( /* ... */)}

こんな風にすると、mapが実際に評価されるときにはCursorが閉じてたりなんかして悲しいことになったりするので要注意です。