関数型インタフェースとは、1 つの抽象メソッドを持つインタフェースを指します。 Single Abstract Method(SAM)とも呼ばれます。
たとえば、以下のようなインタフェースを関数型インタフェースといいます。
public interface FunctionalInterface {
public abstract void doSomething(String text);
}
この記事では、関数型インタフェースの基本的な使用方法と例について説明します。
1. 関数型インタフェースを使用する理由
関数型インタフェースを使用する理由は、Javaのラムダ式は関数型インタフェースでしかアクセスできないからです。
たとえば、次のコードでは、変数funcはラムダ式で生成されたオブジェクトを指しています。
doSomething()
に引数として文字列を渡すと、ラムダ式で定義されているようにログに出力されます。
public interface FunctionalInterface {
public abstract void doSomething(String text);
}
FunctionalInterface func = text -> System.out.println(text);
func.doSomething("do something");
Output:
do something
これまで開発をしながら匿名クラスでオブジェクトを作成したことがありました。 以下のコードは、匿名クラスを使用してリファクタリングしたコードです。関数型インタフェースとラムダ式で匿名クラシックを簡単に表現したと考えられます。
FunctionalInterface func = new FunctionalInterface() {
@Override
public void doSomething(String text) {
System.out.println(text);
}
};
func.doSomething("do something");
まとめると、関数型インタフェースを使用することはラムダ式で作成されたオブジェクトにアクセスするためです。 上記の例のように、ラムダ式を使用するたびに関数型インタフェースを毎回定義するのは不便であるため、Javaからライブラリとして提供するものがあります。
2. 基本関数型インタフェース
Javaがデフォルトで提供する関数型インタフェースには、次のものがあります。
- Runnable
- Supplier
- Consumer
- Function<T, R>
- Predicate
これ以外にも様々なものがあります。 Javaのjava.util.functionパッケージで定義されています。ご希望の場合はこちらをご確認ください。
さて、例で代表的な関数型インタフェースをどのように活用できるかを見てみましょう。
3. Runnable
Runnable
は引数を受けず、戻り値もないインタフェースです。
public interface Runnable {
public abstract void run();
}
以下のコードのように使用できます。
Runnable runnable = () -> System.out.println("run anything!");
runnable.run();
Output:
run anything!
Runnable は run()
を呼び出す必要があります。関数型インタフェースごとに run()
のような実行メソッド名が異なります。
インタフェースの種類ごとに作成された目的が異なり、その目的に合った名前を実行メソッド名で決めたからです。
4. Supplier
Supplier<T>
は引数を受けずにT型のオブジェクトを返します。
public interface Supplier<T> {
T get();
}
以下のコードのように使用できます。 get()
メソッドを呼び出す必要があります。
Supplier<String> getString = () -> "Happy new year!";
String str = getString.get();
System.out.println(str);
Output:
Happy new year!
5. Consumer
Consumer<T>
はT型のオブジェクトを引数として受け取り、戻り値はありません。
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
以下のコードのように使用できます。 accept()
メソッドを使用します。
Consumer<String> printString = text -> System.out.println("Miss " + text + "?");
printString.accept("me");
Output:
Miss me?
また、 andThen()
を使用すると、2 つ以上の Consumer を連続して実行できます。
Consumer<String> printString = text -> System.out.println("Miss " + text + "?");
Consumer<String> printString2 = text -> System.out.println("--> Yes");
printString.andThen(printString2).accept("me");
Output:
Miss me?
--> Yes
6. Function
Function<T, R>
はT型の引数を受け取り、R型のオブジェクトを返します。
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
次のように使用できます。 apply()
メソッドを使用します。
Function<Integer, Integer> multiply = (value) -> value * 2;
Integer result = multiply.apply(3);
System.out.println(result);
Output:
6
compose()
は、2 つの Function を組み合わせて新しい Function オブジェクトを作成するメソッドです。
注意すべき点は andThen()
とは実行順序が逆です。 compose()
に引数として渡される Function が最初に実行され、その後に呼び出されるオブジェクトの Function が実行されます。
たとえば、次のように compose を使用して新しい Function を作成できます。 apply を呼び出すと add が最初に実行され、その後 multiply が実行されます。
Function<Integer, Integer> multiply = (value) -> value * 2;
Function<Integer, Integer> add = (value) -> value + 3;
Function<Integer, Integer> addThenMultiply = multiply.compose(add);
Integer result1 = addThenMultiply.apply(3);
System.out.println(result1);
Output:
12
7. Predicate
Predicate<T>
はT型引数を受け取り、結果としてbooleanを返します。
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
次のように使用できます。 test()
メソッドを使用します。
Predicate<Integer> isBiggerThanFive = num -> num > 5;
System.out.println("10 is bigger than 5? -> " + isBiggerThanFive.test(10));
Output:
10 is bigger than 5? -> true
and()
と or()
は他の Predicate と一緒に使われます。
直観的に and()
は 2 つの Predicate が true のとき true を返し、 or()
は 2 つのうち 1 つだけ true なら true を返します。
Predicate<Integer> isBiggerThanFive = num -> num > 5;
Predicate<Integer> isLowerThanSix = num -> num < 6;
System.out.println(isBiggerThanFive.and(isLowerThanSix).test(10));
System.out.println(isBiggerThanFive.or(isLowerThanSix).test(10));
Output:
false
true
isEqual()
は static メソッドで、引数として渡されるオブジェクトと同じかどうかをチェックする Predicate オブジェクトを作成します。
次のように使用できます。
Predicate<String> isEquals = Predicate.isEqual("Google");
isEquals.test("Google");
Output:
true
8. References
Related Posts
- Java - AtomicReference使用方法
- Java - CountDownLatchを使用する方法、および例
- Java - ScheduledThreadPoolExecutor使用方法
- Java - SummaryStatisticsの使用方法(count、min、max、average)
- Java8 - 関数型インタフェース (Functional Interface) について
- Java - String配列をint配列に変換する
- Java - ArrayList要素の値を変更する方法、replaceAll()
- Java - 2つのリストが同じかどうかを比較
- Java - 配列から特定のIndex要素を削除する3つの方法
- Java - HashMapソート、4つの方法
- Java - 文字列を配列に変換する方法
- Java - ArrayListが空であることを確認する3つの方法
- Java - ArrayListの巡回、4つの方法
- Java - ArrayListの最大値、最小 値を見つける
- Java - ArrayListの合計、平均値の計算
- Java - HashMap巡回、3つの方法
- Java - do whileとwhileの違い
- Java - Lambda式と関数型インタフェース
- Java - List empty(null)チェック、3つの方法
- Java - ArrayListの初期化、4つの方法
- Java - Stream.reduce()の使い方と例
- Java - 2つのマップを結合する(merge、putAll)
- Java - java.util.Dateをjava.sql.Dateに変換する
- Java - ArrayListをStringに変換する
- Java - ClassNotFoundExceptionの発生原因と解決策
- Java - 非静的メソッドは静的コンテキストから参照できません
- Java - NoSuchMethodErrorの原因と解決策
- Java - JSONライブラリを使用する方法(JSONObject、JSONArray)
- Java - byte[]配列をFileに保存
- Java - byte[]の配列をStringに変換
- Java - ファイルのアクセス権を確認し、変更
- Java - 一時フォルダ(Temp directory)パスを取得する
- Javaでシェルスクリプトを実行
- Java - Streamを配列に変換する
- Java - リスト重複排除、2つの方法