関数型インターフェースとは
関数型インターフェースは抽象メソッドが1つだけ定義されてるインターフェース
その抽象メソッドをラムダ式で楽に実装しようというもの
つまり、関数型インターフェースとラムダ式は基本セットで使う(ずっ友)
抽象メソッドが1つだけ定義されていればよい(2つ以上はダメ)ので以下のメソッドはあってもなくてもいい
- 具象メソッド
- defaultメソッド
- privateメソッド
- staticメソッド
ローカルクラス
メソッドやコンストラクタや初期化子の中に定義されたクラス
アクセスできるのは定義されたメソッドやコンストラクタだけなのでアクセス修飾子をつける必要がない
メソッドやコンストラクタ内で値が変わる変数にはアクセスできない(値が再代入されない、実質的finalな変数にしかアクセスできない)
以下の例でいうと
- メソッド呼び出し時にnumが作られる
- あるタイミングでローカルクラスのインスタンス生成
このときにnumの値をコピっといて持っておく
このような処理を辿るため、あとで「値がちがう!」と整合性がなくなるのを防ぐため
void method() {
int num = 10;
num++; // ← ここで実質的finalでなくなる
class Local {
void print() {
System.out.println(num); // エラー!
}
}
}
アクセス修飾子をつけるとエラー
SE16からはレコードも定義できるようになったぽい
匿名クラス
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("匿名クラスから実行中!");
}
};
r.run(); // 出力: 匿名クラスから実行中!
クラス宣言と同時にインスタンス化をする
newの後に書いたクラスを継承した無名のサブクラスを即席で作成する
名前がないので独自のコンストラクタ定義はできない
ラムダ式
BiConsumer<Integer, Integer> printer = (a, b) -> System.out.println(a + b);
printer.accept(2, 4); // 出力: 6
関数型インターフェースの抽象メソッドを実装するときに使う
構文は (引数) -> 処理 の形。
引数がない場合は ()、戻り値を返す処理を右側に書く。
1文のみなら {} を省略でき、JavaScriptの無名関数に似ている。
関数型インターフェースの種類
①java.util.function.Supplierインターフェース
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Supplierは提供者。
抽象メソッドであるgetメソッドが定義されている
実装方法
Supplier<List<String>> supplier = () -> new ArrayList();
提供者なので引数を渡さずに値を返してくれるいいやつ
戻り値はSupplierの右に書いたジェネリクスと同じ型が返る必要がある
上記でいうとList<String>
の型が返る
呼び出し方法
List<String> list = supplier.get();
②java.util.function.Consumerインターフェース
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
Consumerは消費者
抽象メソッドであるacceptメソッドが定義されている
実装方法
Consumer<String> printer = str -> System.out.println("こんにちは " + str);
消費者なので受け取った引数を使っちゃって処理をする
引数はConsumerの右に書いたジェネリクスと同じ型を渡す必要がある
呼び出し方法
printer.accept("Nicole"); // 出力:こんにちは Nicole
③java.util.function.Predicateインターフェース
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Predicateは断定する
抽象メソッドはtestメソッドが定義されている
実装方法
Predicate<String> isEmpty = s -> s.isEmpty();
断定する人なので受け取った引数を使って判断をしてくれる裁判官
戻り値はboolean型
引数はPredicateの右に書いたジェネリクスと同じ型を渡す必要がある
呼び出し方法
System.out.println(isEmpty.test("")); // true
④java.util.function.Functionインターフェース
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
抽象メソッドはapplyメソッドが定義されている
実装方法
Function<String, Integer> lengthFunction = s -> s.length();
ジェネリクスで指定された型に従って受け取った引数を使って値を返してくれる商人
上記でいうとString型のお金を渡してInteger型の商品をもらう
呼び出し方法
System.out.println(lengthFunction.apply("hello")); // 5
⑤5java.util.function.UnaryOperatorインターフェース
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
// Function<T, T> を継承している
}
Functionを継承しているため抽象メソッドはapplyメソッドが定義されている
実装方法
UnaryOperator<String> toUpper = s -> s.toUpperCase();
ジェネリクスで指定された型に従って受け取った引数を使って値を返してくれる両替機
上記でいうとString型のお金を渡してString型のお金を返す
呼び出し方法
System.out.println(toUpper.apply("hello")); // HELLO