Monday, March 21, 2016 / kotlin

kotlin のコレクションでフィルタ実装を差し替えたい場合

たとえば、カレントディレクトリにある 拡張子 .png を持ったファイル名のみのリストを作成したい場合。 kotlin では以下のように書ける。

import java.io.File
val list = File(".").listFiles().filter( { it.name.endsWith(".png") } ).map( {it.name} )

次に png ではなく jpg を列挙したい場合...

import java.io.File
val list = File(".").listFiles().filter( { it.name.endsWith(".jpg") } ).map( {it.name} )

と書けばいいのだが、次に bmp の場合は・・・となるとこのフィルタ実装部分だけを切り離したくなる。

もし Groovy ならば以下のようになる。

def pngFilter = { it.name.endsWith(".png") }
def jpgFilter = { it.name.endsWith(".jpg") }
def bmpFilter = { it.name.endsWith(".bmp") }

def list = new File(".").listFiles().findAll( pngFilter ).collect( {it.name} )

kotlin も同じノリで実装してみると・・・

val pngFilter = { it.name.endsWith(".png") }
val list = File(".").listFiles().filter( pngFilter ).map( {it.name} )

コンパイラに怒られる。

error: type inference failed: inline fun <T> kotlin.Array<out T>.filter(predicate: (T) -> kotlin.Boolean): kotlin.collections.List<T>

どうやら predicate: (T) -> kotlin.Boolean という関数型がくることを期待しているよ、ということらしい。

(T) の部分は、今の文脈では File がくるところなので、File に置き換えて、pngFilter に型を明示的に指定するコードに修正。

//val pngFilter:(predicate:File)->kotlin.Boolean = { it.name.endsWith(".png") }
val pngFilter:(File)->kotlin.Boolean = { it.name.endsWith(".png") }
val list = File(".").listFiles().filter( pngFilter ).map( {it.name} )

これで意図通り作動するようになった。