Java 8 Lambdas: Pragmatic Functional Programming (English Edition)
- 作者:Richard Warburton
- 出版社/メーカー: O'Reilly Media
- 発売日: 2014/03/18
- メディア: Kindle版
関数型プログラミングスタイルによって何が変わるかというと、
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") );
次回
に続く