0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

関数型インタフェースとラムダ式

Posted at

関数型インタフェースとは

実装が必要な抽象メソッドを1つだけ持つインタフェースのこと。

Logic.java
interface Logic {
  void run(String name); // ← 抽象メソッドを一つだけもつ。
}

ラムダ式

関数型インタフェースの実現を、簡潔なコードで行うための記述方法。
ラムダ式を使用すれば、新たにクラスを宣言せずにインタフェースを実現できる。
※ ラムダ式以外にも、匿名クラスを使用する方法もある。

ラムダ式を使用する場合と使用しない場合のコードで比較してみます。
☟のような、Logicインタフェースを使用するServiceクラスを定義します。

Service.java
class Service {
  private Logic logic;

  public Service(Logic logic) {
    this.logic = logic;
  }

  public void do(String name) {
    this.logic.run(name);
  }
}

ラムダ式を使用しない場合

HelloLogic.java
class HelloLogic implements Logic {
  @Override 
  public void run(String name) {
    System.out.println("Hello, " + name); // 抽象メソッドを実装する。
  }
}
Main.java
class Main {
  public static void main(String[] args) {
    Logic logic = new HelloLogic();
    Service service = new Service(logic);
    service.run("Qiita");
  }
}

ラムダ式を使用する場合

※ HelloLogicクラスは無し

Main.java
class Main {
  public static void main(String[] args) {
    Logic logic = (name) -> {
      System.out.println("Hello, " + name); // 抽象メソッドを実装する。
    };
    Service service = new Service(logic);
    service.run("Qiita");
  }
}

関数型インタフェースとラムダ式はどうして必要?

例えば☝のコードでいえば、次のように書いても同じ動作になります。

Service.java
class Service {
  public void do(String name) {
    System.out.println("Hello, " + name);
  }
}
Main.java
class Main {
  public static void main(String[] args) {
    Service service = new Service();
    service.run("Qiita");
  }
}

このコードで問題なのは、

  1. 『処理の全体の流れ』を担当するクラス(Service.java)と
  2. 『具体的なアルゴリズム』を担当するクラス(HelloLogic.java)が

ひとつになってしまっていることです。

逆に、『ラムダ式』の項で書いた2パターンのプログラムは、
『処理の全体の流れ』と『具体的なアルゴリズム』を分けて実装しています。
この実装方針は『GoF』デザインパターンの一つである『Strategy』パターンと呼ばれています。

『付け替え可能なアルゴリズムを実装する』デザインパターンともいわれるこのプログラミングのように
『具体的なアルゴリズム』が『処理の全体の流れ』の外で定義されていると
アルゴリズムだけを変更することが容易になります。

では、☟のコードではどうでしょうか。

EchoHello.java
class EchoHello {
  public void run(String name) {
    System.out.println("Hello, " + name);
  }
}
Service.java
class Service {
  private EchoHello echo;

  public Service() {
    this.echo = new EchoHello();
  }

  public void do(String name) {
    echo.run(name);
  }
}
Main.java
class Main {
  public static void main(String[] args) {
    Service service = new Service();
    service.run("Qiita");
  }
}

たしかに『具体的なアルゴリズム』の実装をEchoHelloクラスに外だし出来ていますが、
このコードの問題点は『全体の処理の流れ』のクラスと『具体的なアルゴリズム』のクラスが直接結びついてしまっていることです。
もしEchoHelloクラスに何らかの変更があったときに、Serviceクラスも修正する必要性が出てくる可能性があります。

このコードを、関数型インタフェースを使用して実装することで、
アルゴリズム間に共通の型をもたせ、クラス間の直接の結びつきを避けることができます。
ServiceクラスからはLogicインターフェイスという型だけを意識すればよくなるので、
アルゴリズムを変更しても全体の処理を修正する必要はなくなります。

さらに、関数型インタフェースをラムダ式で実現すれば、クラスをやみくもに増やすことなく
Strategyパターンに沿ったプログラムを組むことができるというわけです。

まとめ

関数型インタフェースとラムダ式を使用することで、
Strategyパターンに則った、変更に強いプログラムを
簡潔なコードで実装することができます。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?