AndroidをKotlinとRetrolamda+Lombokとで作る場合の比較

[Kotlinアドベントカレンダー](http://www.adventar.org/calendars/857)の4日目とかそんなの
昨日は [com4dcさん](https://twitter.com/com4dc) の [自分を追い込んで頑張る方向で。WP用のKotlinのSytnaxHighlighter作ったという報告](http://dev.classmethod.jp/tool/wordpress-syntax-highlighter-plugin-for-kotlin/) で
明日は [chibatchingさん](https://twitter.com/chibatching) の Kotlin + GAEな感じ です

## はじめに

* なんでScalaと比較しないの？
  * Kotlinは[「Scalaで満足しているならいらない子」](https://kotlinlang.org/docs/reference/comparison-to-scala.html)ってKotlinサイドが言っているので…比較するまでもないだろ？(とりあえず煽っとく方向でｗ）
  * 俺がもう一年以上仕事でScala書けてないから…（現職程度にゆるい環境でScala書けるお仕事知りませんか？w 無いですね）
* なんでRetrolambda使用のAndroidでの比較なの？
  * 仕事で書いている環境が（完全に自分の趣味で） そうだから
* 普通はこう書く！って感じの間違いは指摘でも編集リクエストでも構わないのでください（むしろお願いします）


## 比較内容

* ありがちななんかしらのCallbackをあれする
* 拡張関数的な奴
* Apkのサイズ
* stacktrace比較

あたり


## 環境

### 共通

* Android Studio 1.4.1で吐き出したテンプレベース
    * 2.0? 知らない子ですね (2015/11/23現在)
* JDK 1.7.0_79

### javaのような何かとretrolambda

* JDK 1.8.0_51

後はbuild.gradle見てください


## ありがちななんかしらのCallbackをあれする

と言ってもOnClickListenerだけじゃ面白味が無いので、[ここをだいたいそのまま使って](http://dev.classmethod.jp/smartphone/android/okhttp-retrofit-rxandroid/)今日の天気を表示する…ぐらいやることに…

あー、そういやこれLicenseとかあるのかしら？

起動部分は面倒なのでFloatingActionButtonタップ時にやるよ（ぇ

### 素のジャバっぽいなにか

[この辺](https://github.com/daneko/kotlin-adventcalender-2015/blob/master/plain/src/main/java/com/github/daneko/android/plain/MainActivity.java#L28-L49)
上記をまんまコピったのを置いとく

```java
fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(final View view) {
        WeatherService.currentTokyoWeather()
                .subscribeOn(Schedulers.newThread())
                .map(new Func1<WeatherResponse.Weather, Snackbar>() {
                    @Override
                    public Snackbar call(WeatherResponse.Weather weather) {
                        return Snackbar
                                .make(view, weather.getMain(), Snackbar.LENGTH_LONG)
                                .setAction("Action", null);
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<Snackbar>() {
                    @Override
                    public void call(Snackbar bar) {
                        bar.show();
                    }
                });
    }
});


```

サイコーですね

またレスポンスの型定義とかも…
ステップあたりでお金が貰える世界ならほんとサイコー

### 魔改造BeforeAfter(Retrolambdaとか使うよ)

[こうなる](https://github.com/daneko/kotlin-adventcalender-2015/blob/master/retrolambda/src/main/java/com/github/daneko/android/retrolambda/MainActivity.java#L25-L31)

```java
fab.setOnClickListener(view -> WeatherService.currentTokyoWeather()
        .subscribeOn(Schedulers.newThread())
        .map(weather -> Snackbar
                .make(view, weather.getMain(), Snackbar.LENGTH_LONG)
                .setAction("Action", null))
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(Snackbar::show);
```

特に新鮮味は無いですね

どちらかというと
* [Response用に定義しているクラス](https://github.com/daneko/kotlin-adventcalender-2015/blob/master/retrolambda/src/main/java/com/github/daneko/android/retrolambda/WeatherResponse.java)
* [Observable#flatMapIterable](https://github.com/daneko/kotlin-adventcalender-2015/blob/master/retrolambda/src/main/java/com/github/daneko/android/retrolambda/WeatherService.java#L31) 使っている部分
のほうがすっきり感出ている

前者はLombok、後者はretrolambdaのチカラ…

Unitテストは大体一緒になるので省いた

### Kotlin版

plain（？）なコードをIDEのチカラで変換してチョットイジっただけ
interfaceを変換したらoperatorって出てきたんだけどなんだろう？
なお消した(おぃ

```kotlin
fab.setOnClickListener { view ->
    WeatherService.currentTokyoWeather().
            subscribeOn(Schedulers.newThread()).
            map {
                Snackbar.
                        make(view, it.main, Snackbar.LENGTH_LONG).
                        setAction("Action", null)
            }.
            observeOn(AndroidSchedulers.mainThread()).
            subscribe { it.show() }
}
```

魔改造版と同様、
* [Response用に定義しているクラス](https://github.com/daneko/kotlin-adventcalender-2015/blob/master/kotlin/src/main/kotlin/com/github/daneko/android/kotlin/WeatherResponse.kt)
* [Observable#flatMapIterable](https://github.com/daneko/kotlin-adventcalender-2015/blob/master/kotlin/src/main/kotlin/com/github/daneko/android/kotlin/WeatherService.kt#L26)使っている部分
のほうがすっきり感出ている

mapの中の改行はIDE任せでフォーマットした結果
Kotlinのstyleガイドとかは有るかもしれないが調べてないので、そんなもんなんだろう…と勝手に納得している

lombokの`@Value`使ったので、だいたい一緒だろうということで `data class`にしといた
copyメソッドと同様のものを魔改造版で生やすなら`@Wither`付ける必要あるけど…

個人的に良いと思った部分はここ
[ヒアドキュメント(とKotlinの場合言うのか知らないけど)があるので、テスト用のレスポンスを可読性高いまま書ける点](https://github.com/daneko/kotlin-adventcalender-2015/blob/master/kotlin/src/test/kotlin/com/github/daneko/android/kotlin/WeatherServiceTest.kt#L25-L71)
完全に個人的な好みなのですが、(Unit)テスト用のDummyデータもコード中に置きたい派なので…
理由を書き出すと完全に別エントリになるのでやめます

### この章？のまとめ

* lombokとか使わなくてもイミュータブルなクラスがさくっと作れるの楽
  * だが別にこの程度では魔改造で「コードを書くだけなら」どうとでもなる
* 実はGroovy同様androidのbuild:gradle:1.3.1(正式名称なんだろ？)に対応してないんじゃないかとか思ってたけどそんなこと無くてよかった
    * あくまで2015-11-17現在
    * Groovyも 0.3.7-SNAPSHOTを自分でBuildすれば com.android.test plugin **以外** は動く
* ヒアドキュメントは便利(Kotlinに限った話ではない)
* 思いの外面倒になりそうだったNullableに、今回の例だとぶつからなくて何も確認できていない…
    * KAnnotatorを使ってみるチャンスが…

## 拡張関数的な奴

書いている時に[こんな資料](https://speakerdeck.com/magiepooh/10-extension-functions-of-kotlin-in-android-development)を見つけた

よし比較しよう…

### ViewからSnackBarを生成する拡張関数を用意

同一パッケージなのでImportも要らないしで、[差分の通りすっきり](https://github.com/daneko/kotlin-adventcalender-2015/compare/extensions?expand=1#diff-630d39ee403c92eafd06cebc14305215L25)…

### LombokにもExtensionMethodあるよ！

[こんなコード](https://github.com/daneko/kotlin-adventcalender-2015/compare/fail?expand=1#diff-5c2f5087849f4de836bc47f45edf2c97R1)をかけば…

と思ったのですが…これlambda式内だと使えないっぽいですね…
(何気に使ったことなかった)

retrolambda先生によってlambda式が$Lambda.classみたいな感じに生成されるはず
で、別にこのクラスにアノテーションは付与されていないよね？
というオチっぽいです(´・ω・｀)
(ちゃんとは調べていないのであれですが…)

なおエラーはこんな感じ

```text
/kotlin-adventcalender-2015/retrolambda/src/main/java/com/github/daneko/android/retrolambda/MainActivity.java:32: error: cannot find symbol
                .map(weather -> view.snack(weather.getMain()))
                                    ^
```

### まとめ

拡張関数的な奴は自分の確認した範囲ではKotlinの勝ち

ぐぬぬ…

## Apkサイズ

とりあえずリリースビルドで比較しましょう

Kotlinのpruguard設定は公式ドキュメントでは特に言及されていないので何も設定していない
(kotlin側が上手にやっているのだろう)

retrolambdaとかlombokとか使っているとlintで

```text
java.lang.UnsupportedOperationException: Unknown ASTNode child: LambdaExpression
```
とか出てきて、なんだかコンソール見るのが辛くなるけどそこは精神力でカバーしましょう
[こういうのあるけど使ったことはない](https://github.com/evant/android-retrolambda-lombok)

```text
find -E ./ -regex ".*(debug|release)\.apk" | xargs ls -la -h
-rw-r--r--  1 daneko  46682944   2.2M Nov 24 14:37 .//kotlin/build/outputs/apk/kotlin-debug.apk
-rw-r--r--  1 daneko  46682944   1.0M Nov 24 14:38 .//kotlin/build/outputs/apk/kotlin-release.apk
-rw-r--r--  1 daneko  46682944   1.8M Nov 24 14:38 .//plain/build/outputs/apk/plain-debug.apk
-rw-r--r--  1 daneko  46682944   1.0M Nov 24 14:39 .//plain/build/outputs/apk/plain-release.apk
-rw-r--r--  1 daneko  46682944   1.8M Nov 24 14:39 .//retrolambda/build/outputs/apk/retrolambda-debug.apk
-rw-r--r--  1 daneko  46682944   1.0M Nov 24 14:40 .//retrolambda/build/outputs/apk/retrolambda-release.apk
```

うん、誤差

もちろんアプリとしてKotlinの機能を使っていけば、strip出来るものがへるでしょうから、
サイズはDebug側に寄っていくはず…

とは言うものの、debugビルドでの差分を見ても`0.4MB`なわけで…
これを気にすることは今だと無さそう？

気にする側に倒すと、retrolambda側は優秀ですねｗ

ちなみにmethod数でkotlinの部分だけ抜き出すとこんな感じ

```text
kotlin: 7786
    annotation: 12
    concurrent: 31
    internal: 9
    io: 332
    jvm: 600
        functions: 23
        internal: 563
            unsafe: 2
    math: 22
    properties: 58
    reflect: 58
    support: 15
    test: 109
    text: 117
    util: 2
```

なお、今回の構成だとほとんどStripされて存在すら無いレベルだった…

### まとめ

もっと大きなアプリで比較したいけど… 多分大抵の人は気にしなくて良い気がする


## StackTrace

なにかあったらそこから追いかけるしか無い！

ということで理由はないけど、Snackbar表示前にRuntimeException!

あくまでTrace追ってみたいだけなので、そもそも色々とオカシイ！ってのは無視してください

### [素](https://github.com/daneko/kotlin-adventcalender-2015/blob/exception/plain/src/main/java/com/github/daneko/android/plain/MainActivity.java#L33-L63)

なんていうかその…どうしてこうなった
ステップ数でおカネが（ｒｙ


```java

final RuntimeException cause = new RuntimeException("cause");
WeatherService.currentTokyoWeather()
        .subscribeOn(Schedulers.newThread())
        .map(new Func1<WeatherResponse.Weather, Snackbar>() {
            @Override
            public Snackbar call(WeatherResponse.Weather weather) {
                return Snackbar
                        .make(view, weather.getMain(), Snackbar.LENGTH_LONG)
                        .setAction("Action", null);
            }
        })
        .observeOn(AndroidSchedulers.mainThread())
        .flatMap(new Func1<Snackbar, Observable<Snackbar>>() {
            @Override
            public Observable<Snackbar> call(Snackbar snackbar) {
                return Observable.error(new RuntimeException("test", cause));
            }
        })
        .subscribe(new Action1<Snackbar>() {
                       @Override
                       public void call(Snackbar bar) {
                           bar.show();
                       }
                   },
                new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Log.w("test", throwable);
                    }
                });

```


表示した結果


```text
java.lang.RuntimeException: test
    at com.github.daneko.android.plain.MainActivity$1$3.call(MainActivity.java:48)
    at com.github.daneko.android.plain.MainActivity$1$3.call(MainActivity.java:45)
    at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:55)
    at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:208)
    at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:170)
    at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
    at android.os.Handler.handleCallback(Handler.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:155)
    at android.app.ActivityThread.main(ActivityThread.java:5511)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1029)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:796)
    at dalvik.system.NativeStart.main(Native Method)
 Caused by: java.lang.RuntimeException: cause
    at com.github.daneko.android.plain.MainActivity$1.onClick(MainActivity.java:33)
    at android.view.View.performClick(View.java:4102)
    at android.view.View$PerformClick.run(View.java:17085)
    at android.os.Handler.handleCallback(Handler.java:615) 
    at android.os.Handler.dispatchMessage(Handler.java:92) 
    at android.os.Looper.loop(Looper.java:155) 
    at android.app.ActivityThread.main(ActivityThread.java:5511) 
    at java.lang.reflect.Method.invokeNative(Native Method) 
    at java.lang.reflect.Method.invoke(Method.java:511) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1029) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:796) 
    at dalvik.system.NativeStart.main(Native Method) 
```

まあ追えそうですね
CauseByの一つ目でonClick指してますし

proguardかけた状態

```text
java.lang.RuntimeException: test
    at com.github.daneko.android.plain.d.call(MainActivity.java:1048)
    at b.d.a.j.a(OperatorMap.java:55)
    at b.d.a.w.call(OperatorObserveOn.java:1208)
    at b.d.c.g.run(ScheduledAction.java:55)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
 Caused by: java.lang.RuntimeException: cause
    at com.github.daneko.android.plain.a.onClick(MainActivity.java:33)
    at android.view.View.performClick(View.java:5198)
    at android.view.View$PerformClick.run(View.java:21147)
    at android.os.Handler.handleCallback(Handler.java:739) 
    at android.os.Handler.dispatchMessage(Handler.java:95) 
    at android.os.Looper.loop(Looper.java:148) 
    at android.app.ActivityThread.main(ActivityThread.java:5417) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

```

現状の設定なら余裕な感じ

### [retrolambdaとか](https://github.com/daneko/kotlin-adventcalender-2015/blob/exception/retrolambda/src/main/java/com/github/daneko/android/retrolambda/MainActivity.java#L28-L38)

```java
final RuntimeException cause = new RuntimeException("cause");
WeatherService.currentTokyoWeather()
        .subscribeOn(Schedulers.newThread())
        .map(weather -> Snackbar
                .make(view, weather.getMain(), Snackbar.LENGTH_LONG)
                .setAction("Action", null))
        .observeOn(AndroidSchedulers.mainThread())
        .<Snackbar>flatMap(bar -> Observable.error(new RuntimeException("test", cause)))
        .subscribe(Snackbar::show, th -> {
            Log.w("test", th);
        });
```

```text
java.lang.RuntimeException: test
    at com.github.daneko.android.retrolambda.MainActivity.lambda$null$1(MainActivity.java:35)
    at com.github.daneko.android.retrolambda.MainActivity.access$lambda$2(MainActivity.java)
    at com.github.daneko.android.retrolambda.MainActivity$$Lambda$5.call(Unknown Source)
    at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:55)
    at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:208)
    at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:170)
    at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
    at android.os.Handler.handleCallback(Handler.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:155)
    at android.app.ActivityThread.main(ActivityThread.java:5511)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1029)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:796)
    at dalvik.system.NativeStart.main(Native Method)
 Caused by: java.lang.RuntimeException: cause
    at com.github.daneko.android.retrolambda.MainActivity.lambda$onCreate$3(MainActivity.java:28)
    at com.github.daneko.android.retrolambda.MainActivity.access$lambda$0(MainActivity.java)
    at com.github.daneko.android.retrolambda.MainActivity$$Lambda$1.onClick(Unknown Source)
    at android.view.View.performClick(View.java:4102)
    at android.view.View$PerformClick.run(View.java:17085)
    at android.os.Handler.handleCallback(Handler.java:615) 
    at android.os.Handler.dispatchMessage(Handler.java:92) 
    at android.os.Looper.loop(Looper.java:155) 
    at android.app.ActivityThread.main(ActivityThread.java:5511) 
    at java.lang.reflect.Method.invokeNative(Native Method) 
    at java.lang.reflect.Method.invoke(Method.java:511) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1029) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:796) 
    at dalvik.system.NativeStart.main(Native Method) 

```

lambda…の文字が…
CauseByの3つ目でonClickが出てきます
コード生成というかバイナリ弄る系の宿命ですね…

```text
java.lang.RuntimeException: test
    at com.github.daneko.android.retrolambda.MainActivity.a(MainActivity.java:14035)
    at com.github.daneko.android.retrolambda.c.call(Unknown Source)
    at b.d.a.j.a(OperatorMap.java:55)
    at b.d.a.w.call(OperatorObserveOn.java:1208)
    at b.d.c.g.run(ScheduledAction.java:55)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
 Caused by: java.lang.RuntimeException: cause
    at com.github.daneko.android.retrolambda.MainActivity.a(MainActivity.java:8028)
    at com.github.daneko.android.retrolambda.a.onClick(Unknown Source)
    at android.view.View.performClick(View.java:5198)
    at android.view.View$PerformClick.run(View.java:21147)
    at android.os.Handler.handleCallback(Handler.java:739) 
    at android.os.Handler.dispatchMessage(Handler.java:95) 
    at android.os.Looper.loop(Looper.java:148) 
    at android.app.ActivityThread.main(ActivityThread.java:5417) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
```

proguardの結果、逆にあっさりしてて読みやすそうな気がしてしまう…ｗ


本来の該当箇所である部分のonClickの部分で `Unknown Source`になっているのは、やっぱり追いかけるの大変になるだろうなぁ…
(実際生成されたクラスだろうからコードは無いはずだしｗ)


### [kotlin](https://github.com/daneko/kotlin-adventcalender-2015/blob/exception/kotlin/src/main/kotlin/com/github/daneko/android/kotlin/MainActivity.kt#L26-L32)

```kotlin
val cause = RuntimeException("cause")
WeatherService.currentTokyoWeather().
        subscribeOn(Schedulers.newThread()).
        map { view.snack(it.main) }.
        observeOn(AndroidSchedulers.mainThread()).
        flatMap { snack -> Observable.error<Snackbar>(RuntimeException("test", cause)) }.
        subscribe { it.show() }
```

retrolambda版と比較して`flatMap`部分での型を明示している場所が異なるんだけど…
ここで明示しないと怒られた…
というだけで深い意味はない

retrolambda版はどっちでもイケる
どう推論が効くか…なんだろうけど、所詮ニワカプログラマーの俺にとってはそんなもんなんだろう…で納得
(だれか詳しい人が丁寧に説明していくれないかなぁ|дﾟ)ﾁﾗｯ)

```text
java.lang.RuntimeException: test
     at com.github.daneko.android.kotlin.MainActivity$onCreate$1$2.call(MainActivity.kt:31)
     at com.github.daneko.android.kotlin.MainActivity$onCreate$1$2.call(MainActivity.kt:16)
     at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:55)
     at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:208)
     at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:170)
     at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
     at android.os.Handler.handleCallback(Handler.java:615)
     at android.os.Handler.dispatchMessage(Handler.java:92)
     at android.os.Looper.loop(Looper.java:155)
     at android.app.ActivityThread.main(ActivityThread.java:5511)
     at java.lang.reflect.Method.invokeNative(Native Method)
     at java.lang.reflect.Method.invoke(Method.java:511)
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1029)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:796)
     at dalvik.system.NativeStart.main(Native Method)
  Caused by: java.lang.RuntimeException: cause
     at com.github.daneko.android.kotlin.MainActivity$onCreate$1.onClick(MainActivity.kt:26)
     at android.view.View.performClick(View.java:4102)
     at android.view.View$PerformClick.run(View.java:17085)
     at android.os.Handler.handleCallback(Handler.java:615) 
     at android.os.Handler.dispatchMessage(Handler.java:92) 
     at android.os.Looper.loop(Looper.java:155) 
     at android.app.ActivityThread.main(ActivityThread.java:5511) 
     at java.lang.reflect.Method.invokeNative(Native Method) 
     at java.lang.reflect.Method.invoke(Method.java:511) 
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1029) 
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:796) 
     at dalvik.system.NativeStart.main(Native Method) 
```

お、素のやつと同じような感じ…


```text
java.lang.RuntimeException: test
    at com.github.daneko.android.kotlin.c.call(MainActivity.kt:1031)
    at c.d.a.j.a(OperatorMap.java:55)
    at c.d.a.w.call(OperatorObserveOn.java:1208)
    at c.d.c.g.run(ScheduledAction.java:55)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
 Caused by: java.lang.RuntimeException: cause
    at com.github.daneko.android.kotlin.a.onClick(MainActivity.kt:26)
    at android.view.View.performClick(View.java:5198)
    at android.view.View$PerformClick.run(View.java:21147)
    at android.os.Handler.handleCallback(Handler.java:739) 
    at android.os.Handler.dispatchMessage(Handler.java:95) 
    at android.os.Looper.loop(Looper.java:148) 
    at android.app.ActivityThread.main(ActivityThread.java:5417) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

```

良い


### まとめ

Kotlin優秀じゃねーか…


## 感想

2年前に試してみようとした時はWeb上のコンソールで動く奴がコンパイルは通らねえし、なんか色々と面倒だった記憶があります
今回触ってみてそれなりに使えるな（何故上から目線…）と思いました

~~本当は今回くらい簡単なやつで「ザッケンナコラー」みたいな状態になることを望んでいたのですが…~~
今後何かのやつでは採用したいなぁ…と思ったり思わなかったりしました

ただ、Androidの開発ツール周りは **イラつくレベルで更新される** ので、
* 新しい開発機能がついた！
* が、Kotlin(のplugin)で動くとは言っていない
というパターンは今後発生すると思います(まあKotlinに限った話ではないけど)

しかもこれ書いている時（2015/11/23）とかに2.0の話が出るし…ってことは…

なのでまあ「Productに反映させる」のならKotlin1.0が正式にでたあと、
* Androidの開発ツール更新時にどういう追従方法を取るのか
を一度確認してから…のほうが無難かも…
(この辺はGroovy/Robospockなどで過去に痛い目を見ているので…)

とは言うものの、この手のことは言い出すと「素」以外使えなくなるので…
もちろんProductだからこそ、なにか特別な理由がない限り「最新版を即座に反映させる必要はない」はずですが…
ほら…試したいじゃん…ねぇ…

関連として
* Jack and Jillでjava8サポートが来た場合にどうなるか
あたりも気になるところ…

それとやはり **Scalaと比べて本当に学習コスト/入門の敷居は低いか？** という点は個人的には払拭出来ていません
(そもそもScalaと比べて簡単だよーみたいなのがセールスポイントの一つだったような？)
まあその辺は[エバンジェリストであるタロウ先生](https://twitter.com/ngsw_taro)の[2012年後半から更新されていない日本語解説サイト](https://sites.google.com/site/tarokotlin/)が1.0に合わせて更新されることで、敷居がぐっと下がるのでは…と期待しています

今回のサンプルはエラーハンドリングとか基本何もしてないので、使う際は自己責任でどうぞ

## 手を付けたかったけど付けなかった

### Delegates

名前的にlomokのDelegateと比較しても面白そうだったけど、意味合い的に違うっぽいのでやめた

### KAnnotator

個人的な好みでNullSafe機構のあれがウザいと思っている人間です

なので、FunctionalJava + KAnnotatorでどれくらいうざく **なくなる** だろうか…
とかやりたかったけど、今回のテキトーコードだとそもそも出番が無かった…

## reference

* [今回のコード](https://github.com/daneko/kotlin-adventcalender-2015)
    * エラーハンドリングも何もないので、流用するなら自己責任で 
* [Kotlinのreference](https://kotlinlang.org/docs/reference/)
    * [ヒアドキュメント的な奴](https://kotlinlang.org/docs/reference/basic-types.html#string-literals)
* [OpenWeatherMap](http://openweathermap.org/current)
* [retrofit](http://square.github.io/retrofit/)
* [[Android] OkHTTP + Retrofit + RxAndroid で REST クライアントを実装する](http://dev.classmethod.jp/smartphone/android/okhttp-retrofit-rxandroid/)
* [Retrofit2のRxJavaCallアダプタを試す](http://qiita.com/notice_inc/items/74a7e98b54dd2838c559)
* [JetBrains/kannotator](https://github.com/jetbrains/kannotator)
* [KAnnotatorでJavaライブラリでもNull-Safety機能を使う #Kotlin](http://clomie.hatenablog.com/entry/2013/12/22/130149)
* [10 Extension Functions of Kotlin in Android Development](https://speakerdeck.com/magiepooh/10-extension-functions-of-kotlin-in-android-development)
* [Kotlin言語の日本語解説サイト](https://sites.google.com/site/tarokotlin/)


## 2015/12/03 追記

上記から、「なんだ使えるじゃーん」と思って実際手元で動作確認するAndroidアプリに使って一瞬ハマったところのメモ

### 拡張関数で中途半端にシグネチャが被ると辛い？

kotlinは[いろんな便利メソッドを拡張関数として提供](https://github.com/JetBrains/kotlin/tree/0ffce063565b534e845fe736ee23656debbc659a/libraries/stdlib/src/generated)してくれているようです。
で、この中に[これ](https://github.com/JetBrains/kotlin/blob/0ffce063565b534e845fe736ee23656debbc659a/libraries/stdlib/src/generated/_Collections.kt#L1163)があります。

なんてことはない、`Iterable#map` を作ってくれているだけですね。

さて、ここでFunctionalJavaの[Option](https://github.com/functionaljava/functionaljava/blob/v4.4/core/src/main/java/fj/data/Option.java#L44)を見てみましょう

Iterableを実装しています

で[Option#map](https://github.com/functionaljava/functionaljava/blob/v4.4/core/src/main/java/fj/data/Option.java#L197)を見ます

引数に取る[Fはこんなの](https://github.com/functionaljava/functionaljava/blob/v4.4/core/src/main/java/fj/F.java#L41)です

なのでSAM変換により

```kotlin

Option.fromNull(value).map{ v -> ... }.orSome(default)

```

と書けそうな気がします…

が、コンパイルは通りません
[この辺だけを読むとイケそうですけど、ダメ](https://kotlinlang.org/docs/reference/extensions.html#extensions-are-resolved-statically)

**完全に推測** ですが、下記の理由だと思います

* 拡張関数で[`fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R>`](https://github.com/JetBrains/kotlin/blob/0ffce063565b534e845fe736ee23656debbc659a/libraries/stdlib/src/generated/_Collections.kt#L1163)がOptionにも生える
* `optionValue.map{a -> b}` 的な呼び出しをする
* Optionには上記の`#map(Function1)`と既に実装されている`#map(F)`が存在する
* Function Literalが引数として渡され、`#map(Function1)`が拡張関数により与えられているため、`F`へのSAM変換はしない(拡張関数によって与えられたほうが型としてあってしまうし)
* 結果Option型でなく[kotlinのList型](https://github.com/JetBrains/kotlin/blob/fafca76ac53705f677844a791550b192c6ef6996/core/builtins/native/kotlin/Collections.kt#L129)が返る

じゃあどうするか

```kotlin
Option.fromNull(value).map(F<A,B>{ v -> ... }).orSome(default)
```

これならコンパイラは間違えないですね…
なんだろう…この惜しい感じ…

#### 2015/12/28 追記

1.0.0-beta-3595 -> 1.0.0-beta-4584 に上げたら直り（？）ました
ただ[ReleaseNote](https://github.com/JetBrains/kotlin/releases/tag/build-1.0.0-beta-4583)みてもどれだか分からん…


### Calling Kotlin from Java

[ここのドキュメント](https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html)読んだだけだと、Function Literalで定義されたメソッドをJavaから呼び出す方法が載ってないんだけど…
常識なんでしょうか？

Function Literal は [この辺に変換されるっぽい](https://github.com/JetBrains/kotlin/blob/a55f9feacbf3e85ea064cc2aee6ecfd5e699918d/core/runtime.jvm/src/kotlin/jvm/functions/Functions.kt#)ので、

```kotlin
class Hoge {
  companion object {
    // これのfun版が分からん…
    val sum: (Int, Int) -> Int = { x, y -> x + y }

    fun run(f : () -> Unit){f()}
}
```

みたいなのがあったら

```java
Hoge.Companion.getSum().invoke(1,2);

Hoge.Companion.run(new Function0<Unit>() {
    @Override
    public Unit invoke() {
        return Unit.INSTANCE;
    }
});
```

みたいな感じで呼び出せる

なんだけど、どこかにLiteralはFunction…になるって書いてあるんだろうか…

まあIDE経由で見れば問題は無いですね

あとJava側から呼び出す前提なら`@JvmStatic`つけたほうが親切ですね


### Function Literal は カリー化された奴はない？

日本語がオカシイ気がしますがつまりはこういうことです

Functional Java の [Ord#ord](https://github.com/functionaljava/functionaljava/blob/v4.4/core/src/main/java/fj/Ord.java#L168) を見てみましょう

これをretrolambda(ていうかjava8のラムダ式)で使うには

```java
Ord.<A>ord(a -> b -> /*なんかOrderingを返すようなあれ */)
```

になります

これをIDEのチカラでKotlinに変換するとこんな感じ

```kotlin
Ord.ord<A> { a -> { b -> /*なんかOrderingを返すようなあれ */ } }
```

別に行けそうなんですがだめ…

じゃあどうするか
Functional Java の [FNFunctionsにcurryメソッド](https://github.com/functionaljava/functionaljava/blob/v4.4/core/src/main/java/fj/F2Functions.java#L35)があるのでこんな感じならとりあえず…

```kotlin
Ord.ord<A>(F2Functions.curry{ a:A, b:A -> /*なんかOrderingを返すようなあれ */ } )
```

結局コンパイルが通って（そらそうだ的な形ですが…）、まあ比較的わかりやすくて短いかなぁ…
というのがこの形という…

kotlinに curried 的なメソッドあった気がするけど、どこに現行のDocumentがあるのかわからなかった。
これ結構困る（めんどい）んで、だれか解決方法ください。

#### 2016/01/04 追記

より短く書くという意味では

```kotlin
val ord:Ord<A> = Ord.ord{ a -> fj.F { b -> /*なんかOrderingを返すようなあれ */ } }
```

みたいな感じのほうが短いかな？

この辺やはり型推論の順序とかが関わっているんだろうなぁと勝手に思っていますけど…
どうなんでしょうね？



ということで1.0リリースを待ちましょう（？