はじめまして!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クラスは割愛します)
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);
}
}
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の共通部分を一つのメソッドにまとめるならば、以下のような形になるかと思われます。
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固有の部分以外の共通処理を全て一つにまとめつつ、処理の順番も保障できます。
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)についても、使用例とともにまとめていきたいと思います。
初投稿で拙い文章でしたが、最後までお読みになって下さった方はありがとうございました。