はじめに
本記事ではJavaのラムダ式および関数型インタフェースについて、主要な関数型インタフェースとその具体例について説明していきます。
ラムダ式の基本とその背景について知りたい方は、こちらの記事を参考にしてください。
ラムダ式の基本とその背景
対象者
・ラムダ式および関数型インタフェースを初めて学ぶ初心者
・基礎的な知識を習得済みで、再確認や実践例を通じて理解を深めたい方
関数型インタフェースの実践例
主要な関数型インタフェースの種類とその使い方
Java SEでは汎用性の高い関数型インタフェースがクラスライブラリとしてあらかじめ用意されています。
これらを使用すれば、複雑な処理を除いて開発者自身で関数型インタフェースを定義する必要がなくなります。
特に主要なものは以下の通り。
【主要な関数型インタフェース】
分類 | 関数型インタフェース | 抽象メソッド | 説明 |
---|---|---|---|
引数なし、戻り値なし | Runnable | void run() | 任意の処理を実行する |
引数なし、戻り値あり | Supplier<T> | T get() | 任意の処理を実行し、結果を返す |
引数あり、戻り値なし | Consumer<T> | void accept(T) | 指定された引数で、任意の処理を実行する(戻り値なし) |
BiConsumer<T,U> | void accept(T, U) | 指定された2つの引数で、任意の処理を実行する(戻り値なし) | |
引数あり、戻り値あり | Function<T,R> | R apply(T) | 指定された引数で、任意の処理を実行し、結果を返す |
BiFunction<T,U,R> | R apply(T, U) | 指定された2つの引数で、任意の処理を実行し、結果を返す | |
Predicate<T> | boolean test(T) | 指定された引数で、任意の判定処理を行い、結果をboolean型で返す | |
BiPredicate<T,U> | boolean test(T, U) | 指定された2つの引数で、任意の判定処理を行い、結果をboolean型で返す | |
UnaryOperator<T> | T apply(T) | 指定された引数に対して何らかの演算処理を行い、その結果を返す。 ※引数と戻り値が同一のFunctionインタフェースと同様 |
|
BinaryOperator<T> | T apply(T, T) | 指定された2つの引数に対して何らかの演算処理を行い、その結果を返す。 ※2つの引数と戻り値がすべて同一のBiFunctionインタフェースと同様 |
今回は上記のうち、Runnable
、Supplier
、Consumer
、Function
、Predicate
の5つについて具体例を交えて触れてみたいと思います。
尚、本記事では具体例について触れませんが、BiConsumer
やBiFunction
など、引数を2つ取るインタフェースもあらかじめ用意されています。
Runnableインタフェース
Runnable
インタフェースは「引数なし、戻り値なし」の関数型インタフェースです。
主にスレッドプログラミングで使用されますが、それ以外のケースでも使用可能です。
以下はRunnable
インタフェースをラムダ式で実装し、Hello, World!
を出力する処理を代入しています。
Runnable
インタフェースのrun
メソッドを実行することで、処理が行われます。
【Runnableの具体例①】
public class RunnableExample1 {
public static void main(String[] args) {
Runnable task = () -> System.out.println("Hello, World!");
task.run();
}
}
【出力結果】
Hello, World!
以下は複数のスレッドを使用して、複数のタスクを実行する例になります。
【Runnableの具体例②】
public class RunnableExample2 {
public static void main(String[] args) {
// Runnableを実装したタスク1
Runnable task1 = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("Task 1: Count " + i);
try {
Thread.sleep(400); // 0.4秒間停止
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// Runnableを実装したタスク2
Runnable task2 = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("Task 2: Count " + i);
try {
Thread.sleep(600);// 0.6秒間停止
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// Threadにタスクを登録して実行
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
thread1.start();
thread2.start();
}
}
【出力結果】
Task 1: Count 0
Task 2: Count 0
Task 1: Count 1
Task 2: Count 1
Task 1: Count 2
Task 2: Count 2
Task 1: Count 3
Task 1: Count 4
Task 2: Count 3
Task 2: Count 4
※スレッドの実行順序やタイミングは保証されません。(スケジューリングによるため)
Supplierインタフェース
Supplier
インタフェースは「引数なし、戻り値あり」の関数型インタフェースです。
戻り値を一つ返すため、様々な値を取得する際に使用されます。
以下はSupplier
インタフェースをラムダ式で実装し、Hello, World!
を出力する処理を代入しています。
Supplier
インタフェースのget
メソッドを実行することで、処理が行われます。
【Supplierの具体例①】
public class SupplierExample1 {
public static void main(String[] args) {
Supplier<String> stringSupplier = () -> "Hello, World!";
String s = stringSupplier.get();
System.out.println(s);
}
}
【出力結果】
Hello, World!
以下は現在時刻を取得する例になります。
【Supplierの具体例②】
public class SupplierExample2 {
public static void main(String[] args) {
Supplier<Long> currentTimeSupplier = () -> System.currentTimeMillis();
Long currentTime = currentTimeSupplier.get();
System.out.println("Current Time: " + currentTime);
}
}
【出力結果】
Current Time: 1743750478216
Consumerインタフェース
Consumer
インタフェースは「引数あり、戻り値なし」の関数型インタフェースです。
引数は一つ受け取りますが、戻り値は返さないため、何かしらの副作用を発生させるために使用されます。
以下はConsumer
インタフェースをラムダ式で実装し、変数:message
を出力する処理を代入しています。
Consumer
インタフェースのaccept
メソッドを実行することで、処理が行われます。
【Consumerの具体例①】
public class ConsumerExample1 {
public static void main(String[] args) {
Consumer<String> c = message -> System.out.println(message);
c.accept("Hello, World!");
}
}
【出力結果】
Hello, World!
以下はリスト内の各要素を大文字に変換して出力する例です。
Iterable
インタフェースのforEach
メソッドにConsumer
型の変数:printUpperCase
を渡していますが、これはIterable
インタフェースにforEach
メソッドがデフォルト実装として提供されており、引数としてConsumer
を受け取るように設計されているためです。
【Consumerの具体例②】
public class ConsumerExample2 {
public static void main(String[] args) {
List<String> cities = Arrays.asList("Tokyo", "Osaka", "Chiba");
Consumer<String> printUpperCase = city -> System.out.println(city.toUpperCase());
cities.forEach(printUpperCase);
}
}
【出力結果】
TOKYO
OSAKA
CHIBA
Functionインタフェース
Function
インタフェースは「引数あり、戻り値あり」の関数型インタフェースです。
引数を一つ受け取り、戻り値も一つ返すため、数値の計算結果の取得や文字列の長さの取得等に使用できます。
以下はFunction
インタフェースをラムダ式で実装し、apply
メソッドを実行することで商品の価格から消費税込みの価格を計算しています。
Function<T,R>
のT
は引数、R
は戻り値ですので、以下の場合はともにDouble
型を指定しています。
【Functionの具体例①】
public class FunctionExample1 {
public static void main(String[] args) {
double taxRate = 1.08;
Function<Double, Double> taxFunction = (price) -> price * taxRate;
double price = 1000.0;
double taxPrice = taxFunction.apply(price);
System.out.println(taxPrice);
}
}
【出力結果】
1080.0
以下はString
型の文字列を受け取り、その長さを返す例です。
返す値は数値のため、戻り値の方はInteger
型を指定しています。
【Functionの具体例②】
public class FunctionExample2 {
public static void main(String[] args) {
Function<String, Integer> lengthFunction = (String str) -> str.length();
Integer length = lengthFunction.apply("Hello");
System.out.println(length);
}
}
【出力結果】
5
Predicateインタフェース
Predicate
インタフェースは「引数あり、戻り値あり」の関数型インタフェースです。
引数を一つ受け取り、何らかの処理を行った上で結果をboolean
型で返却します。
以下の例では数値が偶数かどうかを判定しています。
Predicate
インタフェースのtest
メソッドを実行することで判定を行っています。
【Predicateの具体例①】
public class PredicateExample1 {
public static void main(String[] args) {
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4));
System.out.println(isEven.test(7));
}
}
【出力結果】
true
false
また、以下の例では文字列が空かどうかを判定しています。
【Predicateの具体例②】
public class PredicateExample2 {
public static void main(String[] args) {
Predicate<String> isEmpty = str -> str.isEmpty();
System.out.println(isEmpty.test(""));
System.out.println(isEmpty.test("Hello"));
}
}
【出力結果】
true
false
プリミティブ型のインタフェース
java.util.function
パッケージには、引数や戻り値が特定のプリミティブ型に特化した専用のインタフェースが用意されています。
本記事では具体例について触れませんが、以下に代表的なものを示しておきます。
Supplierインタフェースの特化型
引数 | 戻り値 | インタフェース |
---|---|---|
なし | int型 | IntSupplier |
なし | long型 | LongSupplier |
なし | double型 | DoubleSupplier |
Consumerインタフェースの特化型
引数 | 戻り値 | インタフェース |
---|---|---|
int型 | なし | IntConsumer |
long型 | なし | LongConsumer |
double型 | なし | DoubleConsumer |
Functionインタフェースの特化型
引数 | 戻り値 | インタフェース |
---|---|---|
int型 | 総称型 | IntFunction<R> |
long型 | 総称型 | LongFunction<R> |
double型 | 総称型 | DoubleFunction<R> |
総称型 | int型 | ToIntFunction<T> |
総称型 | long型 | ToLongFunction<T> |
総称型 | double型 | ToDoubleFunction<T> |
Predicateインタフェースの特化型
引数 | 戻り値 | インタフェース |
---|---|---|
int型 | boolean型 | IntPredicate |
long型 | boolean型 | LongPredicate |
double型 | boolean型 | DoublePredicate |