[Java] ラムダ式と関数型インターフェース
ラムダ式とは前述の匿名クラスの記述をより簡潔に書くことが出来る記述法で、Java8から導入されました。
また、それに合わせてラムダ式で記述できるいくつかの関数型インターフェースが用意されているので、主要な部分についてまとめていきます。
ラムダ式
構文
インターフェース 変数 = (引数) -> {処理};
定義例
例えば匿名クラスの説明で利用した次のサンプルコードがあります。
https://www.s-watanabe.work/2022/10/java.html#3
interface Itest {
public void doTest();
}
public class AnonymousTest {
public static void main(String[] args) {
Itest itest = new Itest() {
@Override
public void doTest() {
System.out.println("Itest.doTest");
}
};
itest.doTest();
}
}
このコードはラムダ式によって次のように置き換えることが出来ます。
interface Itest {
public void doTest();
}
public class AnonymousTest {
public static void main(String[] args) {
Itest itest = () -> {
System.out.print("Itest.doTest");
};
itest.doTest();
}
}
処理内容が1行の場合、中カッコ{}を省略することができます。
Itest itest = () -> System.out.print("Itest.doTest");
- ラムダ式は関数型インターフェースの要件を満たしているもののみ記述可能(抽象クラスや具象クラスはラムダ式で記述できない)。
- 関数型インターフェースとは、定義されている抽象メソッドが1つだけのインタフェースのこと(staticメソッドやdefaultメソッドは定義されていても問題ない)。
定義例(引数を持つ場合)
interface Itest {
public void doTest(String v);
}
public class AnonymousTest {
public static void main(String[] args) {
Itest itest = (String v) -> {
System.out.println(v);
};
itest.doTest("LambdaTest.doTest");
}
}
引数の型はJVMがインターフェースから推測するため省略できます。
Itest itest = (v) -> {
System.out.println(v);
};
さらに引数が1つの場合、カッコ()も省略できます。
Itest itest = v -> {
System.out.println(v);
};
関数型インターフェース
上述の通り関数型インターフェースとは、抽象メソッドを一つだけ持ったインターフェースを指し、ラムダ式に置き換えることによってコードをシンプルに実装することが出来ます。
Java8から、あらかじめいくつかの関数型インターフェースが用意されており、主要なものについて以下にまとめます。
Supplier
Supplierインターフェースの特徴は、引数を受け取らずに何らかの値を返却します。
インターフェース仕様
パッケージ | java.util.function |
---|---|
インターフェース | Supplier<T> |
戻り値の型 | T |
抽象メソッド | get() |
定義例
import java.util.function.Supplier;
public class LambdaTest {
public static void main(String[] args) {
Supplier<String> supplier = () -> {
return "Supplier.get";
};
System.out.println(supplier.get());
}
}
Consumer
Consumerインターフェースの特徴は、引数を受け取り何らかの処理を実行します。戻り値は返しません。
インターフェース仕様
パッケージ | java.util.function |
---|---|
インターフェース | Consumer<T> |
戻り値の型 | void |
抽象メソッド | accept(T) |
定義例
import java.util.function.Consumer;
public class LambdaTest {
public static void main(String[] args) {
Consumer<String> consumer = (String v) -> {
System.out.println(v);
};
consumer.accept("Consumer.get");
}
}
メソッド参照
上記のコードはメソッド参照により以下のように置き換えることが出来ます。
Consumer<String> consumer = System.out::println;
BiConsumer
Consumerインターフェースが引数を一つしか受け取らないのに対し、BiConsumerインターフェースは二つの引数を受け取り何らかの処理を実行します。
Predicate
Predicateインターフェースの特徴は、引数を受け取り何らかの判定を行い、結果をbooleanで返します。
インターフェース仕様
パッケージ | java.util.function |
---|---|
インターフェース | Predicate<T> |
戻り値の型 | boolean |
抽象メソッド | test(T) |
定義例
import java.util.function.Predicate;
public class LambdaTest {
public static void main(String[] args) {
Predicate<Integer> p = (x) -> {
return x < 10;
};
if (p.test(9)) {
// Some code
}
}
}
BiPredicate
Predicateインターフェースが引数を一つしか受け取らないのに対し、BiConsumerインターフェースは二つの引数を受け取り何らかの判定を行います。
Function
Functionインターフェースの特徴は、引数を受け取り何らかの処理を行い、結果を任意の型で返します。
インターフェース仕様
パッケージ | java.util.function |
---|---|
インターフェース | Function<T, R> Rは戻り値の型 |
戻り値の型 | R |
抽象メソッド | apply(T) |
定義例
import java.util.function.Function;
public class LambdaTest {
public static void main(String[] args) {
Function<Integer, Integer> f = (x) -> {
return x * 10;
};
System.out.println(f.apply(10));
}
}
処理内容が1行の場合、次のように置き換えることが可能です。
Function<Integer, Integer> f = (x) -> x * 10;
合成メソッド
FunctionインターフェースにはandThenとcomposeという合成メソッドが用意されています。Functionインターフェース同士を合成することで、複数の処理をつなぎ合わせて一つの処理として実行することが出来ます。
andThenとcomposeの違いは実行する順番だけで、andThenが指定準通り、composeが指定の逆順に処理を行います。
import java.util.function.Function;
public class LambdaTest {
public static void main(String[] args) {
Function<Integer, Integer> f1 = (x) -> x * 10;
Function<Integer, Integer> f2 = (x) -> x + 5;
System.out.println("andThen: (10 * 10) + 5 = " + f1.andThen(f2).apply(10));
System.out.println("compose: (10 + 5) * 10 = " + f1.compose(f2).apply(10));
}
}
このコードを実行すると次のように表示されます。
java LambdaTest.java andThen: (10 * 10) + 5 = 105 compose: (10 + 5) * 10 = 150
BiFunction
Functionインターフェースが引数を一つしか受け取らないのに対し、BiFunctionインターフェースは二つの引数を受け取り何らかの処理を行い結果を返します。
コメント
コメントを投稿