Kotlinで書いたプログラムをJava7な環境で動かしたら罠にかかったので自分用にメモします。
起きたこと
以下のようにHashmapにforEachで処理を書くと、Java8以降では問題ないのにJava7の環境下ではエラーが発生して実行できません。
val hash = hashMapOf("hoge" to "fuga", "foo" to "bar")
hash.forEach { key, value ->
System.out.println("$key is $value")
}
Exception in thread "main" java.lang.NoClassDefFoundError: java/util/function/BiConsumer
解決方法
forEach{ key, value ->}
を forEach{ (key, value) ->}
とするか、もしくは forEach{ entry -> entry.key}
とする。
動くコード
val hash = hashMapOf("hoge" to "fuga", "foo" to "bar")
hash.forEach { (key, value) ->
System.out.println("$key is $value")
}
どうしてこうなるのか
()のあるなしでそんな違いがあるのかと思いますが、大ありでした。
最初のようにforEach{key, value ->}
と書いた場合、java.util.HashMapのforEachメソッドが直接呼びされます。
このメソッドの定義は以下の通り。
public void forEach(BiConsumer<? super K, ? super V> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key, e.value);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
ご覧の通り、keyとvalueのセットをBiConsumerで受け取っているのがわかります。
このBiConsumerはJava8で導入されたものなので、Java7環境では当然動かなかったということでした。
一方で動くケース(forEach{(key, value) ->}
や forEach{ entry -> }
)ではkotlin.collectionで定義されているforEachメソッドが呼ばれるので、7以前のJavaでも問題なく実行できます。
そろそろJava7の環境もなくなってくると思いますが、もし7で動かす可能性のあるプログラムなら気をつけておきたいところです。