Java 8 – ラムダ式

こんにちは。渡邊です。
Java 8の研究をしています。

ひしだまさんの Java新機能(Javaの変更点)が非常にわかりやすくて勉強になりました。
難しい話はできませんが、実際の開発業務で使うとなった時に知っておくと良さそうなことを記そうと思います。

次のコードにJUnitのテストコードを追記していきます。最終的な LambdaTest.java を添付します。

public class LambdaTest {
    private List input = Arrays.asList(2, 4, 6, 8, 10, 1, 3, 5, 7, 9);

}

次のコードは、リストの要素を自然順序の逆順にソートします。ご存知の通り、これはJava 7以前の書き方の1つで、ラムダ式による記述ではありません。

    @Test
    public void lambda100() {
        //Collections.reverse(input);
        Comparator comparator = new Comparator() {
            @Override
            public int compare(Integer a, Integer b) {
                return b.compareTo(a);
            }
        };
        Collections.sort(input, comparator);
        assertThat(input, is(Arrays.asList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)));
    }

ラムダ式の基本文法は (引数) -> { 処理 } です。lambda100と同じことをラムダ式によって次のよう記述することができます。

    @Test
    public void lambda101() {
        Comparator comparator = (Integer a, Integer b) -> {
            return b.compareTo(a);
        };
        Collections.sort(input, comparator);
        assertThat(input, is(Arrays.asList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)));
    }

Java 8では、Comparator のように抽象メソッドを1つしか持たないインターフェースを関数型インターフェースと呼びます。関数型インターフェースに限り、ラムダ式で記述できます。

ところで、Java 8の Javadocで Comparator を確認すると、メソッドがたくさん追加されていることに気づきます。Java 8ではインターフェースにstaticメソッドを書けるようになりました。また、default修飾子を付けることでディフォルト実装を書けるようになりました。追加されたメソッドはそのいずれかです。それらのメソッドには実装がある為、Comparator は関数型インターフェースの条件を満たしています。
このあたりのことは、Java 8で追加された @FunctionalInterface というアノテーションのJavadocに詳しく書かれています。重要なので、ぜひご確認ください。

関数型インターフェースであることを明示的に示すため、Comparator には @FunctionalInterface が付与されています。関数型インターフェースの条件を満たしていないのに @FunctionalInterface を付与すると(または @FunctionalInterface が付与されている状態で、関数型インターフェースの条件が崩れると)、コンパイルエラーになります。
(@FunctionalInterface が付与されていなくても)関数型インターフェースの条件さえ満たしてれば、ラムダ式で記述できますが、業務ではやめた方がよいと思います。

ラムダ式はより簡潔に記述することができます。引数の型はわかりきっているので、次のように省くことができます。

    @Test
    public void lambda102() {
        Comparator comparator = (a, b) -> {
            return b.compareTo(a);
        };
        Collections.sort(input, comparator);
        assertThat(input, is(Arrays.asList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)));
    }

処理が1文である場合、次のように {} を省略することができます。

    @Test
    public void lambda103() {
        Comparator comparator = (a, b) -> b.compareTo(a);
        Collections.sort(input, comparator);
        assertThat(input, is(Arrays.asList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)));
    }

もちろん変数を介さずに次のように記述することができます。

    @Test
    public void lambda104() {
        Collections.sort(input, (a, b) -> b.compareTo(a));
        assertThat(input, is(Arrays.asList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)));
    }

更に引数が1つの場合は引数を囲う () を省いて a -> System.out.println(a) のように省略できます(例は次回 Stream 編で示す予定です)。

引数がない場合は () の省略はできず、次のように記述します。Runnable を使った例です。

    @Test
    public void lambda201() {
        new Thread(() -> System.out.println("test")).start();
    }

ついでに Comparator のstaticメソッド naturalOrder(自然順序でソートする Comparator を返す)と、defaultメソッド reversed(逆順でソートする Comparator を返す)の例を示します。

    @Test
    public void lambda301() {
        Collections.sort(input, Comparator. naturalOrder());
        assertThat(input, is(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)));
    }

    @Test
    public void lambda302() {
        Collections.sort(input, Comparator. naturalOrder().reversed());
        assertThat(input, is(Arrays.asList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)));
    }

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です