読者です 読者をやめる 読者になる 読者になる

メモっとけ Java 8 Lambdas 〜Chapter2〜


Java 8 Lambdas: Pragmatic Functional Programming

Java 8 Lambdas: Pragmatic Functional Programming

        

関数型プログラミングスタイルによって何が変わるかというと、
The difference is that object-oriented programming is mostly about abstracting over data, while functional programming is mostly about abstracting over behavior.
        

ラムダ式

Swing のアクションリスナーに匿名クラスでActionListenerの実装を入れる例

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent event) {
        System.out.println("button clicked.");
    }
}

     

ラムダ式を使うと以下のように書ける。

button.addActionListener(event -> System.out.println("button clicked."));

        

さらに色々な例

引数なしの関数定義で、Runnable インターフェースの唯一の引数なしメソッド run を定義

Runnable noArguments = () -> System.out.println("Hello World.");

        

複数の文が必要な場合は {} で囲む

Runnable multiStatement = () -> {
    System.out.println("Hello");
    System.out.println("World");
}

     

引数が2つの関数定義

BinaryOperator<Long> add = (x, y) -> x + y;

  

引数の型を指定した例

BinaryOperator<Long> addEcplicit = (Long x, Long y) -> x + y;

        

クロージャではない

匿名クラスで外部の変数を参照するには変数宣言をfinalにする必要がある

final String name = getUserName();
button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent event) {
        System.out.println("Hello " + name);
    }
}

     

ラムダ式だと以下のように書ける

String name = getUserName();
button.addActionListener(event -> System.out.println("Hello " + name));

     

ラムダ式内では変数ではなく値として取り込まれ、つまり暗黙的に final 宣言されたように振る舞う。 そして以下のようにすると「ラムダ式中の変数はfinalか事実上finalでないと駄目!」コンパイルエラーになる。

String name = getUserName();
name = formatUserName(name);
button.addActionListener(event -> System.out.println("Hello " + name));

     

関数インターフェース

ラムダ式の型として使われる、1つの抽象メソッドが定義されたインターフェース 例えば先の例で見た ActionListener は昔からなじみのあるもの。

public interface ActionListener extends EventListener {
    public void actionPerformed(ActionEvent e);
}

     

JDK で提供されるよく使う関数インターフェース

Interface name Arguments Rerurns
Predicate<T> T boolean
Consumer<T> T void
Function<T, R> T R
Supplier<T> None T
UnaryOperation<T> T T
BinaryOperation<T> (T, T) T

     

Predicate は1つの引数を入力として、boolean を返す関数。定義は以下。

public interface Predicate<T> {
    boolean test(T t);
}

     

5 より大きいかを判断する関数。

Predicate<Integer> atLeast5 = x -> x > 5;

     

BinaryOperation は2つの引数を入力として1つの出力を返す関数。

BinaryOperation<Long> addLongs = (x, y) -> x + y;

  

以下のようにするとコンパイルエラーとなる。

BinaryOperation addLongs = (x, y) -> x + y;

  

UnaryOperation は論理否定。

        

ThreadLocal の withInitial() は Supplier 関数を引数に取るので以下のような初期化コードが可能。

public final static ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(
    () -> new SimpleDateFormat("dd-MMM-yyyy") );

     

次回 メモっとけ Java 8 Lambdas 〜Chapter3〜 - A Memorandum に続く