Java8を便利にするためのSF(少しFunctional)なライブラリJL2を作ってみた

Java8をもう少しだけ便利に使うための少しFunctionalなライブラリを書いてみました。

github.com

Java8からStream APIが増えて随分コーディングが楽になったんですが、まだまだ不満があります。

その一つが多値を扱うTupleが無いこと。StreamAPIでmapを使って値をこねくり回してると複数の値を返したい時があります。

HaskellScalaならTupleがあります。 RubyやJSなら動的型付けなので、配列に複数の型が入れれます。

では、Javaでは? Mapでは2つの型までしか扱えませんし、ObjectのListや配列もナンセンスです。本来は都度都度適切な型を定義するのがJava流かもですが、いくらなんでも面倒。

というわけで、ScalaのTupleを移植してみました。合わせて、私が良く使う関数型っぽい処理をユーティリライブラリにまとめて公開しました。

使い方

githubリポジトリを作ってるので、Mavenの場合は下記をpom.xmlに追加してもらえれば利用で可能です。

<repositories>
    <repository>
        <id>koduki-repos</id>
        <url>https://raw.githubusercontent.com/koduki/koduki.github.io/mvn-repo/</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>cn.orz.pascal</groupId>
        <artifactId>jl2</artifactId>
        <version>0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

Tuple

全ての基本となるTupleです。まあ、ほぼScalaからの移植です。下記のように使います。

Tuple2<String, Integer> x = new Tuple2<>("A", 10);
Tuple2<Integer, String> y = $(1, "B"); // '$' はシンタックスシュガー
Tuple4<Integer, String, Boolean, List<Integer>> x = $(1, "A", true, Arrays.asList(2)); // Mapと違っていくらでも型が書ける
Tuple2<Integer, Tuple2<String, String>> z = $(1, $("A", "B")); // 入れ子もOK

配列と違って複数の型を管理できます。$関数を一部使ってますが、これはnew TupleXシンタックスシュガーです。

これが使えるだけでStreamAPIをはじめ使い勝手が随分良くなります。

コレクションビルダー

これは特に関数型は関係無いですが、Javaのコレクションは初期化が面倒なのでScalaっぽい記法のシンタックスシュガーを作りました。

Set<String> set = set("a", "b");
List<String> list = list("a", "b");
Map<Integer, String> map = map($(1, "a"), $(2, "b"));

特に、MapはTupleの組み合わせることで通常より格段と書きやすくなってると思います。

Immutable なコレクション操作

たまにJava再帰を書こうとするとImmutable なコレクション操作ができないので直感的に書きづらかったりします。

なので下記のように簡単に書けるライブラリを作りました。

まあ、この辺はより本格的なライブラリがGuavaとかにあると思うので、そっちの方がより便利に使えると思います。

Set<Integer> set1 = set(1, 2);
Set<Integer> set2 = set(3);
Set<Integer> concatedSet = concat(set1, set2);

// create immutable map
ImmutableMap<String, String> map = imap($("k1", "v1"), $("k2", "v2"));
ImmutableMap<String, String> addedMap = map.put("k3", "v3");
ImmutableMap<String, String> removedMap = map.remove("k2");

JDBC Wrapper for StreamAPI

プロトタイプやシンプルなアプリの場合は、ORM使わずに直にJDBC使うこともそれなりにあると思います。

ただ、ResultSetはStreamAPIに対応してないので書くのが少し面倒。というわけで、対応させてみました。

try (
        Connection con = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
        PreparedStatement pt = con.prepareStatement("select * from persons ");) {
    List<String> ids = QueryStream.of(pt.executeQuery())
            .map(r -> r.getString(1))
            .collect(Collectors.toList());

    assertThat(ids.size(), is(4));
    assertThat(ids.get(0), is("1"));
    assertThat(ids.get(1), is("2"));
    assertThat(ids.get(2), is("3"));
    assertThat(ids.get(3), is("4"));
}

JDBCをもうちょっとモダンに使いたいけど、大げさなライブラリを入れたく無い時に便利です。個人的にはTupleとMapの初期化に次いで良く使う機能です。

まとめ

本格的な関数型サポートを行うライブラリは色々ありますが、そこまで大げさなものは要らないという時に、SFライブラリのJL2はいかがでしょうか?

それでは、Happy Hacking!