あいぼーいの部屋

@I_BOY_1204がJavaやJavaScriptに関して調べた事を忘れない様につらつらと記述する備忘録です。

Java8 Lambdaの型推論

Java8からLambdaが書けるようになって、いろいろと簡単に書くことができるようになったので、
その型推論について勉強してみた。

JavaのLambdaではFunctional Interfaceの実装をLambdaで与えることができる。
例えば、Comparatorの実装に

Comparator<String> comparator = (String a, String b) -> a.compareTo(b)

などと書けるようになった。

そのため、Listのソートなどでは、

list.sort((String a, String b) -> a.compareTo(b));

と書くことで、ソートされる。

ここで、記述したLambdaがどのFunctional Interfaceの実装かの型推論コンパイル時に行なわれている。

実際には、以下のような手順で型推論が行なわれているようだ。

  1. 呼び出したメソッド定義を確認(例では、List.sortのメソッド定義)
  2. メソッド定義からLambdaはComparatorの実装であることがわかる
  3. ComparatorはString型の引数を二つ受け取り、int型の値を返すメソッドの実装をする必要がある
  4. ここで、引数に渡したLambdaに戻って、引数の型/個数・返り値の確認が行われる
  5. Lambdaでは、引数の型は省略できるので、省略している場合は個数の確認が行われる

最後の項目で、引数の型/個数・返り値に問題がなければコンパイルに成功し、
何処かで型が一意に決まらなければ、コンパイルエラーになる。

例えば、以下のような三種類のメソッド定義をしたクラスを準備して、
Lambdaをsortの引数に指定した場合、01, 02は同じ型/個数の引数を取り、
同じ型の返り値を返すため、コンパイルエラーになる。
これは、型推論をした結果、よび出すメソッドが一意に特定できないためである。
※03は返り値の型が他のものと異なるので、型推論可能


01, Comparatorを引数に渡す場合

    public void sort(List<String> list, Comparator<String> c1) {
        list.sort(c1);
    }

02, ToIntBiFunctionを引数に渡す場合

    public void sort(List<String> list, ToIntBiFunction<String, String> biFunction) {
        Comparator<String> comparator = (String a, String b) -> biFunction.applyAsInt(a, b);
        list.sort(comparator);
    }

03, BiFunctionを引数に渡す場合

    public void sort(List<String> list, BiFunction<String, String, String> biFunction) {
        Comparator<String> comparator = (String a, String b) -> Integer.valueOf(biFunction.apply(a, b));
        list.sort(comparator);
    }

また、型推論の祭に、auto boxingなどは考慮されないので、

    Comparator<Integer> c1 = (int a1, int a2) -> {
        if (a1 < a2) return 1;
        else if (a1 == a2) return 0;
        return -1;
    };

などと書くとコンパイルエラーになる。
ちなみに、この場合、引数の型を省略していれば、メソッドの処理部分ではauto boxingされるので、
コンパイルエラーにも実行時エラーにもならずに正しく動いてくれる。
# 省略してもいいところを省略することは変なミスを防ぐ結果にもなるということ

次は、メソッドリファレンスの話を書こうと思う。