Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

Guavaをざっくり紹介

Guava librariesをご存知でしょうか?Googleが開発・使用しているコアライブラリです。OSSであり簡単に入手できます。

このライブラリは結構多機能で、海外の著名プロダクトでもよく見かけるのですが、日本のネットでの知名度はいまいち高くない気がします。もともとの名前(Google Collections)はそこそこ知名度があったと思うのですが、この差はなぜでしょうか?私の観測範囲が狭いだけかもしれませんけども……。
自分が使ってみて思った「へーこんなことができるんだー」をまとめてみますので、共感するところがあればぜひ検証してみてはいかがでしょうか。

何が嬉しいの?

Guavaはできることを増やすライブラリではなく、普段やっていることを短く書くためのライブラリです。Apache commonsなどと近い立ち位置にいると言って良いでしょう。
この手のライブラリは他のライブラリへの依存が少なく捨てやすい上にサイズも小さいので、比較的採用しやすい部類に入ります。ので、深く考える前にまず入れてしまいましょう。ホントの良さなんて実際に使わなきゃわからんもんです。はい。

……もう少しきちんと説明すると、以下のようになるでしょう。

  • コードがビジネスロジックに集中できる
    • すでにテストされているライブラリに頼ることで、テストもビジネスロジックに集中できる
    • コードが読みやすくなり保守性が向上する(かもしれない)
    • オレオレユーティリティの存在意義を無くせる
  • 静的解析の有効性を向上できる
    • イディオム起因の深いネストを一掃できるので、本当にダメな深いネストを見つけやすくなる
      • 分岐も同様
    • コードにおけるビジネスロジックの割合が高いので、コードのカバレッジがの業務シナリオのカバレッジに近くなる(かもしれない)
    • @VisibleForTestingにより、大量のsrc/test/javaを解析することなく不要に広いスコープを見つけられる
    • PreconditionsとJSR305をあわせることで、事前条件のチェック漏れをはじき出せる

Collections

Guavaの前身、Google Collectionsから引き継いだクラス群です。Lists.newArrayList()が有名ですね。他にもたくさんありますので、Java標準のコレクションでは書きづらいなと思ったときにJavadocを読み返すと良いでしょう。

ちなみに自分が使ってみて最も嬉しかったのは、これ↓が

Map<KeyA, Map<KeyB, Value>> nestedMap = ...
Map<KeyB, Value> innerMap = nestedMap.get(keyA);
Value value;
if (innerMap == null) {
    value = null;
} else {
    value = innerMap.get(keyB);
}

以下のように記述できたときでした。

Table<KeyA, KeyB, Value> table = ...
Value value = table.get(keyA, keyB);

Idioms

Guavaのstaticメソッド群を使用することで、長ったらしいイディオムを短く書くことができます。例えば誰もがこういったコードをコピペしたりユーティリティメソッドを使ったりした経験があると思いますが:

} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            logger.info("IOException occured", e);
        }
    }
}

GuavaのCloseables#closeQuietly()を使用することで、こう書けます。深すぎるネストが一掃できるのはいいことです。

} finally {
    Closeables.closeQuietly(reader);
}

他にもPatternFilenameFilterStopwatchなど、誰もが1度は見かけたことがあるコードが入っています。積極的に再利用したいものです。

Preconditions

Guavaの中でも最も使用頻度が高いのが、Preconditionsでしょう。事前条件をチェックするコードを簡潔に記述できます。IDEの設定を変更して、簡単にstatic importできるようにしておくのもひとつの手です。

public void print(File file) {
    Preconditions.checkState(! this.isClosed());
    Preconditions.checkNotNull(file);
    Preconditions.checkArgument(file.isFile());
    Preconditions.checkArgument(file.canRead());
    // ......

Annotation

「可視性をprivateにしたいって?残念だが、それはテストで使ってるんだ」をアピールするためのアノテーション@VisibleForTestingがあります。広すぎるスコープをコード解析で抽出する際の誤判定防止に使用できるでしょう。セマンティックWebならぬセマンティックソースコードですね*1

final class Student {
    @VisibleForTesting
    void sayName() {
        // ......

「インタフェース変わったり消えたりするかもしれないよ」をアピールするためのアノテーション@Betaもありますが、ライブラリユーザーがこのアノテーションを気にしてくれるのかは疑問です。ライブラリの作り手と使い手が同じコミュニティに所属している場合*2はルール決めやコード解析によって一定の役割を果たせるでしょうが、基本的にはJavadocとあまり変わらないモノだと認識したほうがよさそうです。


なおGuavaがjsr305.jarに依存しているので、@NullableなどJSR305のアノテーションも使用できるようになります。

オススメの使い方

成長するライブラリの全貌をつかんで活用するのはわりと無理があると思いますので、まずはPreconditions, Closeables周りから試し、次いで自分のプロジェクトでよく見かける処理をGuavaで簡素化できないか試すと良いでしょう。特にCollectionとIO周りは機能が充実していますので、コードのスリム化を強力に支援してくれるはずです。

*1:Mockito使いとしては@NotFinalForMockingがほしい

*2:Google社内がまさにこの条件を満たしており、何らかの形で活用されているのだろうと推測