LoginSignup
1
0

More than 3 years have passed since last update.

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

Posted at

はじめまして!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)についても、使用例とともにまとめていきたいと思います。
初投稿で拙い文章でしたが、最後までお読みになって下さった方はありがとうございました。

1
0
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
1
0