Kotlinを知ってしまった俺がRoboVMでiOS/Android開発するために導入したもの


# はじめに

iOS/Androidのアプリを開発するのにRoboVM+Kotlinを推したのですが、最終的にRoboVM+Javaになりました。AndroidではJava 8さえ使えません。

環境はRoboVMなので、build.gradleはios/android/coreに分かれています。Android Studioの人は全部１つのbuild.gradleに書けばOKです。

# Retrolambda

Java 8のラムダをJava 7以下でも使えるようにしてくれるものです。これはRoboVMだとプロジェクト作成しただけで最初から使えるようにしてくれます。

# Lombok

KotlinのData Class相当のことをやってくれる@Data, そのImutable版の@Valueが強力です。

```java
// コンストラクタとかGetter/Setter, toString, equals/hashCodeなんかを自動生成してくれる。
@Data
public class Person {
    private final String name;
    private int age;
}
```

```java
// 全部finalなimmutableなクラスならこれ。private finalも省略できる。
@Value
public class Point {
    double x;
    double y;
}
```

Kotlinのval相当のものがあったりします（なんと型推論してくれる！）。

```java
    // final ArrayList<String> list = new ArrayList<String>();
    val list = new ArrayList<String>();
```

あとはコンストラクタ自動生成もよく利用しますね。@Getter, @Setterも便利。

```java
// 全てのインスタンス変数を引数にとって初期化するコンストラクタを自動生成してくれる。
// @Injectアノテーション付けるようにしてDagger2と組み合わせて使うとめちゃ便利。
@AllArgsConstructor(@__(@Inject))
public class MyClass {
    @NonNull private final Hoge hoge;
    @NonNull private final Fuga fuga;

    // ...
}
```

## 利用準備

RoboVMの場合はcore/build.gradleのdependenciesに以下を追記しました。指定したバージョンはその時点の最新です。

```groovy
dependencies {
    // ...
    compile "org.projectlombok:lombok:1.16.6"
}
```

あとはSyncすればGradleが良きに計らってくれます。

## ここがイケてない

ラムダの中で *val* がうまく動かないようです。

# RxJava/RxAndroid

これはKotlinとは関係ないですね。いや最初は後述のstreamsupportを知らなかったんで、Java 8のストリームの代わりにも使えるねってつもりでここに書きました。（参考: [RxAndroidとRetrolambdaで大体Java8をAndroidに持ち込む](http://sys1yagi.hatenablog.com/entry/2015/01/02/101350)）

純粋にオブザーバパターンの拡張ライブラリとしてすごく便利です。これはこれで別途記事を書こうと思います。世間のRx関係の記事は難しいなぁと感じたので。

RxJavaはLombokと同様にcore/build.gradleに追加。

```groovy
dependencies {
    // ...
    compile "io.reactivex:rxjava:1.1.1"
}
```

RxAndroidはandroid/build.gradleに追加。

```groovy
dependencies {
    // ...
    compile "io.reactivex:rxandroid:1.1.0"
}
```

# RxJava-Optional

最初はこれを使おうとしたんですが、後述のstreamsupportにOptionalがあるのに気づいて使うのをやめました。streamsupportを使わないなら以下のようにして導入。内部でRxJavaを使っているのでRxJavaも導入しておく必要があります。

core/build.gradleに追加。

```groovy
dependencies {
    // ...
    compile 'io.reactivex:rxjava:1.1.1'
    compile "com.eccyan:rxjava-optional:1.1.0"
}
```

# streamsupport

こんなに便利なのに日本語情報皆無という[streamsupport](https://sourceforge.net/projects/streamsupport/)はJava 8のstreamのバックポートライブラリです。RxJavaを使えばコレクションからObservableを作れるので、Java 8のstreamっぽいことはできるようになりますが、こっちの方が使いやすいです。

ファイルを覗いてみると、かなりOracleのオープンソース実装(OpenJDK)をそのまま持ってきてるみたいで、ライセンスはクラスパス例外付きのGPLになるはずです。

さすがにJava 8と同じとまではいきません。stream()メソッドが生えてきたりはしません。

```java
// Java 8ならこう書ける
list.stream().forEach(item -> {
    // ...
});
```

ストリームを作るにはユーティリティークラスを利用します。

```java
// メソッドが足されるわけではないので、ユーティリティーを使ってストリームを作る
StreamSupport.stream(list).forEach(item -> {
    // ...
});
```

import staticすればこう書けます。

```java
import static java8.util.stream.StreamSupport.stream;

//...

stream(list).forEach(item -> {
    // ...
});
```

でもこれ、クラッシュしました(T T)。elementDataフィールドがないというエラーですが、これってArrayListが実際にデータを格納している内部フィールドのようです。Javaバージョンの問題？でもOpenJDK 8のソースみてもelementDataって名前のまま。RoboVMと相性悪い？Androidだと動くのかも（まだiOS版開発中でAndroidで試してません）。

このライブラリの最大の問題はドキュメントが全然用意されていないことです。Java 8と同じ部分についてはドキュメントはいらないんですが、違う部分についてはないと最初は使い方がわかりません。

1.4.2の時点ではiOS版は以下のようにシステムプロパティを設定する必要があるそうです。

```java
public static void main(String[] args) {
    System.setProperty("java8.util.Spliterators.assume.oracle.collections.impl", "false");
    // ...
```

他にもMap専用に何かをするならMaps, List専用に何かをするListsなどのユーティリティークラスもあります。これがJava 8標準よりも便利だったりします。

```java
// Mapのイテレート
Maps.forEach(map, (key, value) -> {
    // ...
});

// Map<K, V> を Map<K, X> に変換
Map<K, X> newMap = Maps.transformEntries(map, (key, value) -> {
    // ここで変換後の新しい値を返す
});
```

Java 8だとこうでしょうか。

```java
// Mapのイテレート
map.entrySet().stream().forEach(entry ->
    // 渡されるのがEntryなのでentry.getKey()とかentry.getValue()とかするの面倒
    // ...
});

// Map<K, V> を Map<K, X> に変換
Map<K, X> newMap = map.entrySet().stream().collect(Collectors.toMap(Map.MapEntry::getKey, entry -> {
    // 渡されるのがEntryなのでentry.getKey()とかentry.getValue()とかするの面倒
    // ここで変換後の新しい値を返す
});
```

## 利用設定

core/build.gradleに以下を追記してSyncすればOKです。

```groovy
dependencies {
    // ...
    compile 'net.sourceforge.streamsupport:streamsupport:1.4.2'
}
```

# まとめ

RxJava関係はKotlin使えないからってのとは関係ないので、結局

 - Lombok
 - Retrolambda
 - streamsupport

がKotlinにあってJava 7にないものを足してくれました。このうち後ろ２つはJava 8ならいらないので、つまりLombokが凄いってことですかね。