オリジナル:http://www.javastack.cn/article/2017/jdk8-new-feature-functional-interface/
機能的なインターフェースとは
まず、従来のスレッド作成がどのように記述されているかを見てみましょう
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t1");
}
});
t1.start();
関数型インターフェースの書き方を見てみましょう
Thread t2 = new Thread(() -> System.out.println("t2"));
t2.start();
Runnableインターフェースは、ラムダ式を使用して直接記述できます。これは、Runnableインターフェースが関数型インターフェースであるためです。Runnableのソースコードを見てみましょう。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
インターフェースが機能インターフェース定義アノテーション@FunctionalInterfaceで追加されていることがわかりました。これは、インターフェースが機能インターフェースであることを示しています。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {
}
JDK8では、Runnbaleインターフェースに加えて、Comparator、Callableなどのインターフェースがあり、これらはこのアノテーションを使用して機能インターフェースとして定義されています。
組み込み機能インターフェース
JDK8はいくつかの組み込み機能インターフェースを提供し、多くのAPIの場所で使用でき、ほとんどのアプリケーションを満足させることができます。
//Consumer<T> - T作为输入,执行某种动作但没有返回值
Consumer<String> con = (x) -> {
System.out.println(x);
};
con.accept("hello world");
//Supplier<T> - 没有任何输入,返回T
Supplier<String> supp = () -> {
return "Supplier";
};
System.out.println(supp.get());
//Predicate<T> -T作为输入,返回的boolean值作为输出
Predicate<String> pre = (x) -> {
System.out.print(x);
return x.startsWith("op");
};
System.out.println(": " + pre.test("op, hello World"));
// Function<T, R> -T作为输入,返回的R作为输出
Function<String, String> function = (x) -> {
System.out.print(x + ": ");
return "Function";
};
System.out.println(function.apply("hello world"));
//BinaryOperator<T> -两个T作为输入,返回一个T作为输出,对于“reduce”操作很有用
BinaryOperator<String> bina = (x, y) -> {
System.out.print(x + " " + y);
return "BinaryOperator";
};
System.out.println(" " + bina.apply("hello ", "world"));
カスタム機能インターフェース
1.機能インターフェースをカスタマイズする
@FunctionalInterface
public interface CalcInterface<N, V> {
V operation(N n1, N n2);
}
ここには抽象メソッドが1つしかありません。@ FunctionalInterfaceアノテーションを記述する必要はありません。
2.機能インターフェースを参照する新しいクラスを作成します
public static class NumberOperation<N extends Number, V extends Number> {
private N n1;
private N n2;
public NumberOperation(N n1, N n2) {
this.n1 = n1;
this.n2 = n2;
}
public V calc(CalcInterface<N, V> ci) {
V v = ci.operation(n1, n2);
return v;
}
}
3.機能インターフェースをテストする
private static void testOperationFnInterface() {
NumberOperation<Integer, Integer> np = new NumberOperation(13, 10);
CalcInterface<Integer, Integer> addOper1 = (n1, n2) -> {
return n1 + n2;
};
CalcInterface<Integer, Integer> multiOper1 = (n1, n2) -> {
return n1 * n2;
};
System.out.println(np.calc1(addOper1));
System.out.println(np.calc1(multiOper1));
// 上面的可以简写为
System.out.println(np.calc1((n1, n2) -> n1 + n2));
System.out.println(np.calc1((n1, n2) -> n1 * n2));
}
最終的な出力:
23
130
23
130
機能インターフェース仕様
1. @FunctionalInterfaceは、機能インターフェースは、1つの抽象メソッドのみを持つインターフェースでのみ使用できることを示します。
2.静的メソッド、デフォルトメソッド、およびインターフェイスのObjectクラスをオーバーライドするメソッドは、抽象メソッドとは見なされません。
3. @FunctionalInterfaceアノテーションは必要ありません。インターフェースに抽象メソッドが1つしかない場合、それを記述する必要はありません。デフォルトでは機能インターフェースに準拠しますが、このアノテーションを作成することをお勧めします。インターフェースが機能インターフェース仕様に準拠しているかどうかをコンパイラがチェックします。
正しい機能インターフェースを説明する例を挙げてください。
@FunctionalInterface
public interface CalcInterface<N, V> {
V operation(N n1, N n2);
}
関数スタイルに準拠するいくつかのメソッドを追加しても問題ありません。コンパイラーはエラーを報告しません。
@FunctionalInterface
public interface CalcInterface<N, V> {
V operation(N n1, N n2);
public boolean equals(Object object);
public default void defaultMethod() {
}
public static void staticMethod() {
}
}
この役に立たない@FunctionalInterface関数インターフェースには、Lambda式では使用できない2つの抽象メソッドがあります。
public interface CalcInterface<N, V> {
V operation(N n1, N n2);
V operation2(N n1, N n2);
}
2つの抽象メソッドを持つ@FunctionalInterfaceで注釈が付けられた機能インターフェイスは、コンパイル時にエラーを報告します。
@FunctionalInterface
public interface CalcInterface<N, V> {
V operation(N n1, N n2);
V operation2(N n1, N n2);
}
このための抽象的な方法はなく、コンパイルエラーが報告されます。
public interface CalcInterface<N, V> {
}