Java Gold SE 17のラムダ式・関数型インターフェース対策

標準関数型の比較表と短いコードで、ラムダ式の戻り値・変数参照を確認します。

入力・戻り値・メソッド名を対応させる

インターフェース主なメソッド役割
Predicate<T>boolean test(T t)条件を判定する
Function<T,R>R apply(T t)値を別の型にも変換する
Consumer<T>void accept(T t)値を受け取り、結果値を返さない
Supplier<T>T get()引数なしで値を返す
UnaryOperator<T>T apply(T t)入力と結果が同じ型の変換

BiFunction<T,U,R>のBiは入力が2つという意味です。結果型も指定するため型引数は3つになります。IntPredicateなどのプリミティブ特殊化も、参照型の標準関数型と区別してください。一覧はjava.util.functionの公式APIにあります。

ラムダ式のコンパイル可否を調べる手順

  1. 要求される関数型を特定する。変数の宣言型やメソッド引数の型から、引数の個数・型・戻り値を読みます。
  2. ラムダ本体の形を確認する。値を返す型でブロックを使う場合は、必要な経路でreturnしているか確認します。式の形をしたラムダと、ブロックを使うラムダを同じ書き方にしないようにします。
  3. 外側のローカル変数を確認する。ラムダから参照するローカル変数はfinalまたは実質的にfinalである必要があります。変数の再代入と、参照先オブジェクトの状態変更は区別します。
  4. 検査例外を照合する。ラムダ内で投げる検査例外を、対応する抽象メソッドが許しているか確認します。

関数型インターフェースは、メソッド宣言が1つだけのインターフェースという意味ではありません。default・staticメソッドや、Objectのpublicメソッドに対応する宣言をそのまま数えないようにします。詳細はFunctionalInterfaceの仕様、変数参照などはラムダ式の言語仕様で確認できます。

式とブロック:戻り値がある型ではreturnに注意

Functionのapplyは結果値を返します。式だけのラムダではその式が結果になり、ブロックを使う場合はreturnで返します。

Function<String, Integer> expression =
    text -> text.length();
Function<String, Integer> block =
    text -> { return text.length(); };
System.out.println(expression.apply("Java")); // 4
System.out.println(block.apply("Java")); // 4

text -> { text.length(); }は結果値を返さないため、Function<String, Integer>には代入できません。同じ形でも、戻り値がvoidのConsumer<String>には代入できます。ラムダ本体だけでなく、代入先の抽象メソッドと照合してください。

実質的final:変数の再代入とオブジェクトの変更

ラムダが参照するローカル変数は、finalまたは実質的にfinalである必要があります。参照先のオブジェクトまで変更不可になるわけではありません。

List<String> messages = new ArrayList<>();
Runnable add = () -> messages.add("Java");
add.run();
System.out.println(messages); // [Java]
// messages = new ArrayList<>();
// 上の再代入を加えると、ラムダがコンパイルエラー

この例ではmessagesへの再代入がないため、要素の追加は可能です。宣言にfinalと書いていなくても条件を満たします。規則はラムダ本体の言語仕様で確認できます。

例題2問:関数型の条件と型引数

独立した抽象メソッドはいくつあるか、入力と結果の型は何個指定するかを説明してから答えてください。

例題1:関数型インターフェースの条件

@FunctionalInterfaceを付けられるインタフェースを2つ選んでください。

2つ選択してください

  • A. interface A { int read(); default void close() {} }
  • B. interface D { void open(); void close(); }
  • C. interface B { boolean equals(Object x); void run(); }
  • D. interface C { static void run() {} }
例題1の正解・解説を見る

正解:A・C

defaultとstaticメソッドは抽象メソッドではありません。

defaultとstaticメソッドは抽象メソッドではありません。またObjectのpublicメソッドに対応する宣言はSAMの判定から除外されます。

  • A:抽象メソッドはreadだけです。
  • B:独立した抽象メソッドが2個あります。
  • C:Objectのpublicメソッドに対応するequalsは抽象メソッド数に数えません。
  • D:抽象メソッドが0個なので関数型インタフェースではありません。

確認するポイント:メソッドの宣言数をそのまま抽象メソッド数として数えないでください。

仕様を確認するための公式資料:

問題ID:jg17-de7e1311022257 · この問題の訂正を連絡する

例題2:標準関数型の型引数

標準APIの型引数の個数が正しい宣言を2つ選んでください。

2つ選択してください

  • A. Function<String, Integer> converter;
  • B. Supplier<String, Integer> source;
  • C. UnaryOperator<String, String> edit;
  • D. BiFunction<String, Integer, Boolean> check;
例題2の正解・解説を見る

正解:A・D

型引数の数は、APIが独立に指定できる入力・結果の型に対応します。

型引数の数は、APIが独立に指定できる入力・結果の型に対応します。UnaryOperatorはFunction<T,T>の特殊化です。

  • A:Functionは入力型Tと結果型Rの2型引数です。
  • B:Supplierの型引数は結果型Tの1個です。
  • C:UnaryOperatorは入力と出力が同じ型なので型引数は1個です。
  • D:2入力の型T、Uと結果型Rの3型引数です。

確認するポイント:Biという接頭辞は入力が2個という意味で、型引数が必ず2個という意味ではありません。

仕様を確認するための公式資料:

問題ID:jg17-7058ce5c5c612e · この問題の訂正を連絡する

関数型インターフェースとラムダ式の60問で復習する

関数型インターフェースとラムダ式の演習では、10問から取り組めます。出題数は変更できます。本サイトのこの分類にはネストクラスの問題も含みます。60問は教材の収録数で、試験の出題比率ではありません。

標準関数型が曖昧なら、各型を「何を受け取り、何を返すか」で説明し直します。型の理解を確認した後は、ラムダ式をStreamのfilterやmapの引数として読む練習へ進めます。

関連する学習

本サイトの問題は、Java SE 17を学ぶためのオリジナル練習教材です。本試験の過去問やOracle公式の教材ではありません。編集・訂正の方針をご確認ください。