関数型インタフェースとは
実装が必要な抽象メソッドを1つだけ持つインタフェースのこと。
interface Logic {
void run(String name); // ← 抽象メソッドを一つだけもつ。
}
ラムダ式
関数型インタフェースの実現を、簡潔なコードで行うための記述方法。
ラムダ式を使用すれば、新たにクラスを宣言せずにインタフェースを実現できる。
※ ラムダ式以外にも、匿名クラスを使用する方法もある。
ラムダ式を使用する場合と使用しない場合のコードで比較してみます。
☟のような、Logicインタフェースを使用するServiceクラスを定義します。
class Service {
private Logic logic;
public Service(Logic logic) {
this.logic = logic;
}
public void do(String name) {
this.logic.run(name);
}
}
ラムダ式を使用しない場合
class HelloLogic implements Logic {
@Override
public void run(String name) {
System.out.println("Hello, " + name); // 抽象メソッドを実装する。
}
}
class Main {
public static void main(String[] args) {
Logic logic = new HelloLogic();
Service service = new Service(logic);
service.run("Qiita");
}
}
ラムダ式を使用する場合
※ HelloLogicクラスは無し
class Main {
public static void main(String[] args) {
Logic logic = (name) -> {
System.out.println("Hello, " + name); // 抽象メソッドを実装する。
};
Service service = new Service(logic);
service.run("Qiita");
}
}
関数型インタフェースとラムダ式はどうして必要?
例えば☝のコードでいえば、次のように書いても同じ動作になります。
class Service {
public void do(String name) {
System.out.println("Hello, " + name);
}
}
class Main {
public static void main(String[] args) {
Service service = new Service();
service.run("Qiita");
}
}
このコードで問題なのは、
- 『処理の全体の流れ』を担当するクラス(
Service.java
)と - 『具体的なアルゴリズム』を担当するクラス(
HelloLogic.java
)が
ひとつになってしまっていることです。
逆に、『ラムダ式』の項で書いた2パターンのプログラムは、
『処理の全体の流れ』と『具体的なアルゴリズム』を分けて実装しています。
この実装方針は『GoF』デザインパターンの一つである『Strategy』パターンと呼ばれています。
『付け替え可能なアルゴリズムを実装する』デザインパターンともいわれるこのプログラミングのように
『具体的なアルゴリズム』が『処理の全体の流れ』の外で定義されていると
アルゴリズムだけを変更することが容易になります。
では、☟のコードではどうでしょうか。
class EchoHello {
public void run(String name) {
System.out.println("Hello, " + name);
}
}
class Service {
private EchoHello echo;
public Service() {
this.echo = new EchoHello();
}
public void do(String name) {
echo.run(name);
}
}
class Main {
public static void main(String[] args) {
Service service = new Service();
service.run("Qiita");
}
}
たしかに『具体的なアルゴリズム』の実装をEchoHello
クラスに外だし出来ていますが、
このコードの問題点は『全体の処理の流れ』のクラスと『具体的なアルゴリズム』のクラスが直接結びついてしまっていることです。
もしEchoHello
クラスに何らかの変更があったときに、Service
クラスも修正する必要性が出てくる可能性があります。
このコードを、関数型インタフェースを使用して実装することで、
アルゴリズム間に共通の型をもたせ、クラス間の直接の結びつきを避けることができます。
Service
クラスからはLogic
インターフェイスという型だけを意識すればよくなるので、
アルゴリズムを変更しても全体の処理を修正する必要はなくなります。
さらに、関数型インタフェースをラムダ式で実現すれば、クラスをやみくもに増やすことなく
Strategyパターンに沿ったプログラムを組むことができるというわけです。
まとめ
関数型インタフェースとラムダ式を使用することで、
Strategyパターンに則った、変更に強いプログラムを
簡潔なコードで実装することができます。