Androidとかいうjavaっぽい何かを触っていますけど、
奴はlambdaとか使えないしlambda禁止令がどうとか以前の問題で辛いですね
ScalaでAndroidを!といってもapkのサイズは肥大化するし、多分仕事でOKでないところのほうが多いと思います
そんな昨今、retrolambdaというかなり素敵なものを教わったのでメモ
なお、QiitaにすでにAndroidでラムダ式を使いたいというエントリーが5月に上がっています
一刻も早くサンプルコード見たい人はこちらへ
ハマったその1
OS:Linux Mint 17 と mac OSX 10.9.4 で確認
とりあえずgradle-retrolambdaのREADMEに従えば大丈夫ですが…
retrolambdaそのものを最新(1.6.1)指定にするとコンパイルに失敗します
多分このissueあたりですかね
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:compileRetrolambdaDebug'.
> Process 'command '/usr/lib/jvm/java-8-oracle/bin/java'' finished with non-zero exit value 1
1.6.0指定の時は大丈夫だったので、retrolambdaConfigは指定しない(defaultの1.4.0)かコンパイルできるものを指定しましょう
その他Known Issuesは見ときましょう
とりあえず retrolambda.incremental は false にしておくのが無難なようですね
ハマったその2
AndroidStudioでいざBuildしてみたら
Execution failed for task ':app:patchAndroidJar'.
> Retrolambda: null/jre/lib/rt.jar does not exist, make sure that the environment variable JAVA_HOME or JAVA8_HOME, or the gradle property retrolambda.jdk points to a valid version of java8.
みたいなエラーががが
で、consoleでコンパイルしたら通る!みたいな。
これに関してはこちらへ
懸念点
Java6or7とJava8の2つがコンパイル環境として必要になります
gradle-retrolambdaでは
JAVA6_HOME/JAVA7_HOME/JAVA8_HOME みたいな感じでそれぞれexportしていれば楽ですけど、
例えばみんなの開発マシンにそれを強要したり、CI環境もそういう準備をしたり…という部分がちょっとハードル高いかも?
仮想環境構築もchefやansibleでらくらく!という人たちだけならなんの問題も無いでしょう。
とりあえず動かす
こんなコードが動きます。
そういやScalaでAndroidアプリ書いても、View#senOnClickListener とかには無名クラス突っ込む必要あったな…
java8様様です
view.findViewById(R.id.button1).
setOnClickListener(v ->
Toast.makeText(getActivity(), "test", Toast.LENGTH_LONG).show()
);
だが物足りない
正直これだけでは物足りないと思います。
特にListの処理とか…
結局StreamApiが使えるわけではないのでよくサンプルでありそうなこんなコードを書いてもコンパイルで死にます
List<Integer> list = Arrays.asList(1,2,3,4);
Integer sum = list.stream()
.map(i -> i * 2)
.reduce(0,(x,y) -> x + y);
Error:(64, 31) error: cannot find symbol method stream()
Error:Execution failed for task ':app:compileDebugJava'.
> Compilation failed; see the compiler error output for details.
retrolambdaはあくまでもラムダ構文のサポートですので
もちろんそれのあるなしは大きいですけどね
でもきっとあなたのAndroidアプリにはGuavaあたりが入っていると思います
なのでこんな感じで書けるようになりますね
FluentIterable.from(Arrays.asList(1, 2, 3, 4)).
filter(i -> i % 2 == 0).
transform(i -> i * 2).
toList();
new Function とか書かなくて良いし、これがAndroidで動くとは!!
すでにGuavaに依存している部分は今すぐ書き直したいですね!
View#setOnClickListenerとかがスッキリ書ける部分よりも、
こっちのほうが嬉しいところも多々ありますね!?
素晴らしい時代だ!!
だが待ってほしい
reduce/foldが無いなんて…Guavaさん信じてたのに…
ってことがよくありますよね。
このままではIntのListの合計すらままならない…
LombokのExtensionMethodで生やすか?
いやしかし…
そうだFunctionalJavaだ!
結果がこうなります
final Integer sum = fj.data.List.list(1, 2, 3, 4).
filter(i -> i % 2 == 0).
map(i -> i * 2).
foldLeft(x -> y -> x + y, 0);
よく考えたらGuavaのOptionalにbindほしいなぁ…というのもFunctionalJavaを入れるだけで解決!
もう自分で追加しなくていいんだ…w
final…もめんどい
Lombokのvalを使いましょう
以下こうなる
val sum = fj.data.List.list(1, 2, 3, 4).
filter(i -> i % 2 == 0).
map(i -> i * 2).
foldLeft(x -> y -> x + y, 0);
Scalaを使わずともここまで出来る時代だったのか!
IDEで赤くなるのは置いといて…w
終わりに
個人の趣味で作る分にはなんの問題も無いと思いますが、
- retrolambdaとfunctionaljavaを入れたいんです!
ってのを周りに説得するほうが大変な気がします。
なのでこっそり足すことをオススメします(責任は取りません)
まーでもretrolambdaぐらい入れたいなぁ…
Scalaで書けるなら個人的にそっちのほうが望ましいんですけどね。