【はじめてのJava】ラムダ式と関数型インタフェース【匿名クラスとラムダ式編】
はじめてのJava
このシリーズでは、初めてJavaやプログラミングを勉強する方向けに、Javaによるプログラミングの基礎を説明していきます。
目標レベルは、Javaの資格試験の一つである「Oracle Certified Java Programmer, Silver」(通称Java Silver)に合格できる程度の知識の習得です。
はじめてJavaやプログラムに触れる方にもできるだけわかりやすい解説を心がけていきます。
ラムダ式
今回の記事から、「ラムダ式」について解説していきます。
ラムダ式はJava8から新しく追加された機能になっていて、従来のプログラムを少し特殊な方法で記述するものです。
今回は、ラムダ式についての概要とラムダ式を扱うために必要な「関数型インタフェース」というものについて解説していきます。
目次
ラムダ式とは
「ラムダ式」は、Java8から追加された、プログラムの記述方法です。
一般的には、「関数型インタフェース」と呼ばれるインタフェースや、「StreamAPI」と呼ばれるAPIと併用して利用することで、従来の記述方法よりも簡単にプログラムを書くことができます。
「関数型インタフェース」を利用した記述では、クラスの宣言やインスタンス化を簡潔に行うことができるというメリットや、「StreamAPI」を用いた記述では、ListやMapなどのコレクションの操作を簡潔に行うことができるというメリットがあります。
前回の記事で紹介した「匿名クラス」と同じように、インタフェースを利用する際に実装クラスを作成することなく直接インスタンス化したオブジェクトを作成でき、変数に格納することができます。
ラムダ式を利用するメリットとしては、匿名クラス等を利用した記述に比べてプログラムのソース量を減らすことができることです。
詳しくは後の記事で紹介しますが、ラムダ式には一定の条件に従って、記述を省略することができるという特徴があります。
この記述の省略によって、コード量を大幅に抑えることが可能になります。
また、「アロー演算子」と呼ばれる矢印型の演算子を使って処理を記述することで、処理内容を視覚的にわかりやすく記述することができるというメリットもあります。
基本的なラムダ式の構文は以下のようになっています。
(引数の型名 抽象メソッドの引数名) -> { return 戻り値; }
関数型インタフェース
ラムダ式を利用してインタフェースの実装を行うことができますが、この記述を行うためには条件があります。
ラムダ式を利用することができるインタフェースは「関数型インタフェース」の要件を満たしている必要があります。
「関数型インタフェース」とは、定義されている抽象メソッドが1つだけのインタフェースのことです。
ラムダ式では、抽象メソッドのオーバーライドを行う際に、メソッド名を明確に記述しません。
インタフェースの中に定義されている抽象メソッドが1つだけであれば、オーバーライドしなければならないメソッドはメソッド名を記述しなくても明確であるため、メソッド名の宣言無しでオーバーライドできます。
staticメソッドやdefaultメソッドは定義されていても問題ありません。
抽象メソッドが複数あるインターフェースをラムダ式で実装しようとすると、コンパイルエラーになってしまいます。
// 抽象メソッドが1なので関数型インタフェースになる! interface IntA{ public void methodA(); } // staticメソッドやdefaultメソッドがあるが、抽象メソッドは1つなので関数型インタフェースになる! interface IntB{ public void methodA(); // staticメソッド public static void methodB(){ System.out.println("methodB"); } // defaultメソッド default void methodC(String s){ System.out.println(s); } } // 抽象メソッドが複数あるので関数型インタフェースにはならない! interface IntC{ public void methodA(); public void methodB(); }
ラムダ式を利用する際に、自身で必要な関数型インタフェースを作成しても構いませんが、Javaで汎用的に使える関数型インタフェースが多数用意されています。
メソッドの引数や戻り値の有無やデータ型など、様々なパターンのインタフェースが用意されていますので、必要に応じて利用することでインタフェースを作成する工程を省略することができます。
参考:https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/package-summary.html
また、Javaで用意されている関数型インタフェースでは、引数のデータ型にジェネリクスを利用しているので、インタフェースを利用する時に引数のデータ型を指定することができます。
ここではすべてを紹介することは難しいので、代表的なものをいくつか紹介します。
Supplierインタフェース
Supplierインタフェースには「getメソッド」が定義されています。
getメソッドは、引数なしで、戻り値にジェネリクスで指定したデータ型の値を返すメソッドです。
(例)getメソッドをオーバーライドし、「Hello!」という文字を返すようにする。
class SupplierSample{ public static void main(String[] args){ Supplier<String> sup = () -> "Hello!!"; // Supplierインタフェースに定義されたgetメソッドをオーバーライドしています。 System.out.println(sup.get()); // supオブジェクトのget()メソッドを呼び出す。 } }
Consumerインタフェース
Consumerインタフェースには「acceptメソッド」が定義されています。
acceptメソッドは、引数にジェネリクスで指定したデータ型の値をとり、戻り値なしのメソッドです。
(例)acceptメソッドをオーバーライドし、「Hello,○○!」(○○は引数で渡した文字列)という文字を出力するようにする。
class ConsumerSample{ public static void main(String[] args){ Consumer<String> con = s -> System.out.println("Hello,"+ s +"!"); // Consumerインタフェースに定義されたacceptメソッドをオーバーライドしています。 con.accept("Tanaka"); // conオブジェクトのaccept()メソッドを呼び出す。 } }
Predicateインタフェース
Predicateインタフェースには「testメソッド」が定義されています。
testメソッドは、引数にジェネリクスで指定したデータ型の値をとり、戻り値にboolean型の値を返すメソッドです。
(例)testメソッドをオーバーライドし、引数に渡した文字列の長さが10文字を超えたらtrue、そうでなければfalseを返すようにする。
class PredicateSample{ public static void main(String[] args){ Predicate<String> pre = s -> s.length() > 10; // Predicateインタフェースに定義されたtestメソッドをオーバーライドしています。 System.out.println(pre.test("abcdefghijk")); System.out.println(pre.test("abcde")); // preオブジェクトのtest()メソッドを呼び出す。 } }
Functionインタフェース
Functionインタフェースには「applyメソッド」が定義されています。
applyメソッドは、引数、戻り値の両方のデータ型をジェネリクスで指定することができるメソッドです。
(例)applyメソッドをオーバーライドし、引数に渡した文字列の数値を、Integer型に変換して返すようにする。
class FunctionSample{ public static void main(String[] args){ Function<String,Integer> fun = a -> Integer.parseInt(a); // Functionインタフェースに定義されたapplyメソッドをオーバーライドしています。 System.out.println(fun.apply("123")); // funオブジェクトのapply()メソッドを呼び出す。 } }
匿名クラスとラムダ式編・次回の内容
今回はラムダ式の概要と関数型インタフェースについて紹介しました。
次回は、ラムダ式の構文について解説していきます。
初めてのJavaシリーズの目次はこちら
匿名クラスとラムダ式編の記事一覧はこちら