【Java入門】ラムダ式のすゝめ①：Consumer<T>

はじめまして！natto710と申します。
初投稿させて頂きます。

##ラムダ式って？
Java8より追加された関数型インターフェースの記法です。
アロー演算子を使った独特な記法が特徴的です！
ですが、書籍ベースの学習ですといまいちイメージが掴みづらいものです。
私も、実際の現場でバリバリ使用されている方のコードを見て、イメージがわきました。
##Consumer\<T>とは
4大関数型インターフェースの一つ、
**java.util.function.Consumer\<T>**
です。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/Consumer.html
このインターフェースは**accept(T t)**メソッドを呼び出すことで、引数を受け取って任意の処理を実行させることができます。
戻り値は返さないので、さながらセッターのような処理を実行させる時や、データベースのupdate,delete文を呼び出すDao
を使う時等に便利ですね。
##Consumer\<T>の使用例
例えば、ある一定の処理までは同じ流れなのですが、途中で別々の処理を行い、また共通の処理を行う、といった
処理を書きたいとします。
以下の処理では、配列の合計値を最初に求め、次にmethodAでは最小値を、methodBでは最大値をそれぞれ求め、
続いて平均値を求め、最後に合計、平均、最大、最小を出力します。
（Mainクラスは割愛します）

```Fuga.java
public class Fuga {
  private int[] intArray;
  private int totalInt;
  private int averageInt;
  private int maxInt;
  private int minInt;

  public Fuga(int[] intArray) {
    this.intArray = intArray;
  }
  public int[] getIntArray() {
    return intArray;
  }
  public void setIntArray(int[] intArray) {
    this.intArray = intArray;
  }
  public int getTotalInt() {
    return totalInt;
  }
  public void setTotalInt(int totalInt) {
    this.totalInt = totalInt;
  }
  public int getAverageInt() {
    return averageInt;
  }
  public void setAverageInt(int averageInt) {
    this.averageInt = averageInt;
  }
  public int getMaxInt() {
    return maxInt;
  }
  public void setMaxInt(int maxInt) {
    this.maxInt = maxInt;
  }
  public int getMinInt() {
    return minInt;
  }
  public void setMinInt(int minInt) {
    this.minInt = minInt;
  }
  public void fuga() {
    System.out.println("total  : " + this.totalInt);
    System.out.println("average: " + this.averageInt);
    System.out.println("max    : " + this.maxInt);
    System.out.println("min    : " + this.minInt);
  }
}
```

```Hoge.java
public class Hoge {
  public void methodA(Fuga fuga) {

    // 共通的な処理：fugaのintArrayの合計値を求め、totalに格納する
    int total = 0;
    for (int num : fuga.getIntArray()) {
      total = total + num;
    }
    fuga.setTotalInt(total);

    // methodA固有の処理：intArrayの最小値を求め、minIntに格納する
    int min = fuga.getIntArray()[0];
    for (int num : fuga.getIntArray()) {
      if (min > num) {
        min = num;
      }
    }
    fuga.setMinInt(min);

    // 共通的な処理：intArrayの平均値を求め、averageIntに格納する
    fuga.setAverageInt(fuga.getTotalInt() / fuga.getIntArray().length);

    // 共通的な処理：フィールドの値を全部出力
    fuga.fuga();
  }

  public void methodB(Fuga fuga) {
    // 共通的な処理：fugaのintArrayの合計値を求め、totalに格納する
    int total = 0;
    for (int num : fuga.getIntArray()) {
      total = total + num;
    }
    fuga.setTotalInt(total);

    // methodB固有の処理：intArrayの最大値を求め、maxIntに格納する
    int max = fuga.getIntArray()[0];
    for (int num : fuga.getIntArray()) {
      if (max < num) {
        max = num;
      }
    }
    fuga.setMaxInt(max);

    // 共通的な処理：intArrayの平均値を求め、averageIntに格納する
    fuga.setAverageInt(fuga.getTotalInt()  / fuga.getIntArray().length);

    // 共通的な処理：フィールドの値を全部出力
    fuga.fuga();
  }
}
```
HogeクラスのmethodA、methodBの共通部分を一つのメソッドにまとめるならば、以下のような形になるかと思われます。

```Hoge2.java
public class Hoge2 {
  public void methodA(Fuga fuga) {
    this.calcTotal(fuga);

    // methodA固有の処理：intArrayの最小値を求め、minIntに格納する
    int min = fuga.getIntArray()[0];
    for (int num : fuga.getIntArray()) {
      if (min > num) {
        min = num;
      }
    }
    fuga.setMinInt(min);

    this.calcAverage(fuga);
    this.outPut(fuga);
  }

  public void methodB(Fuga fuga) {
    this.calcTotal(fuga);

    // methodB固有の処理：intArrayの最大値を求め、maxIntに格納する
    int max = fuga.getIntArray()[0];
    for (int num : fuga.getIntArray()) {
      if (max < num) {
        max = num;
      }
    }
    fuga.setMaxInt(max);

    this.calcAverage(fuga);
    this.outPut(fuga);
  }
  // 共通的な処理：fugaのintArrayの合計値を求め、totalに格納する
  private void calcTotal(Fuga fuga) {
    int total = 0;
    for (int num : fuga.getIntArray()) {
      total = total + num;
    }
    fuga.setTotalInt(total);
  }
  // 共通的な処理：intArrayの平均値を求め、averageIntに格納する
  private void calcAverage(Fuga fuga) {
    fuga.setAverageInt(fuga.getTotalInt() / fuga.getIntArray().length);
  }
  // 共通的な処理：フィールドの値を全部出力
  private void outPut(Fuga fuga) {
    fuga.fuga();
  }
}
```

しかし、Consumer\<T>を使えば、以下のようにmethodA、methodB固有の部分以外の共通処理を全て一つにまとめつつ、処理の順番も保障できます。

```Hoge3.java
import java.util.function.Consumer;

public class Hoge3 {
  public void methodA(Fuga fuga) {
    // methodA固有の処理：intArrayの最小値を求め、minIntに格納する
    Consumer<Fuga> calcMin =
        f -> {
          int min = fuga.getIntArray()[0];
          for (int num : fuga.getIntArray()) {
            if (min > num) {
              min = num;
            }
          }
          fuga.setMinInt(min);
        };
    this.methodCommon(fuga, calcMin);
  }

  public void methodB(Fuga fuga) {
    // methodB固有の処理：intArrayの最大値を求め、maxIntに格納する
    Consumer<Fuga> calcMax =
        f -> {
          int max = f.getIntArray()[0];
          for (int num : fuga.getIntArray()) {
            if (max < num) {
              max = num;
            }
          }
          f.setMaxInt(max);
        };
    this.methodCommon(fuga, calcMax);
  }

  private void methodCommon(Fuga fuga, Consumer<Fuga> individualLogic) {
    // 共通的な処理：fugaインスタンスのintArrayの合計値を求め、totalに格納する
    int total = 0;
    for (int num : fuga.getIntArray()) {
      total = total + num;
    }
    fuga.setTotalInt(total);

    // methodA、B固有の処理
    individualLogic.accept(fuga);

    // 共通的な処理：intArrayの平均値を求め、averageIntに格納する
    fuga.setAverageInt(fuga.getTotalInt() / fuga.getIntArray().length);
    // 共通的な処理：フィールドの値を全部出力
    fuga.fuga();
  }
}
```

今回使用した一例は、4大関数型インターフェースの一つ、
**java.util.function.Consumer\<T>**
です。
このインターフェースは**accept(T t)**メソッドを呼び出すことで、引数を受け取って任意の処理を実行させることができます。
使用例では、methodA、methodBでそれぞれ**Consumer\<Fuga>**の関数型オブジェクト
calcMin、calcMaxをそれぞれ定義し、最小/最大値を格納するロジックを記述しています。
そして、methodA、methodB共通部分をまとめた**methodCommon**のパラメータに**Consumer\<Fuga>**型の
変数individualLogicを用意し、個別で処理を行っていた箇所で
**individualLogic.accept(fuga);**
を呼び出してやることで、呼び出し元で定義したロジックが実行されます。
このように、ラムダ式を使えば、処理を変数として閉じ込めておくことができ、呼び出し先の**任意のタイミングで処理を発火**することができます！

他の関数型インターフェース（Function、Predicate、Supplier）についても、使用例とともにまとめていきたいと思います。
初投稿で拙い文章でしたが、最後までお読みになって下さった方はありがとうございました。