亀シミュレータ(ラムダ式バージョン)
それでは次に、Java SE 8のラムダ式を用いて亀シミュレータを書き直してみましょう。
リスト6の中で注目するべきポイントは、Commandの役割を担う Motion インタフェースが、move メソッドをただ1つの抽象メソッドとして持つ関数的インタフェースだという点です。つまり、Motion インタフェースのインスタンスは、LeftMotion クラスや DownMotion クラスのようなConcreteCommandの役割を担うクラスを宣言することなく、ラムダ式によって直接生成できるはずです。
以上の方針に基づき、ラムダ式を用いてプログラムを設計した場合のクラス図は、図5のようになります。
ラムダ式を用いてリスト6を書き換えたプログラムを、リスト7に掲げます。リスト6から変更のある箇所は「★」印で示しています。
import java.util.*;
public class LambdaTurtleSimulator {
public static void main(String[] args) {
// ユーザの入力
String userInput = "↓←←↑↑→→→→↓↓";
// 矢印文字に対応する動作を登録(★ラムダ式を使用)
TurtleMover turtleMover = new TurtleMover();
turtleMover.registerMotion('←', turtle -> turtle.setX(turtle.getX() - 1));
turtleMover.registerMotion('↓', turtle -> turtle.setY(turtle.getY() + 1));
turtleMover.registerMotion('↑', turtle -> turtle.setY(turtle.getY() - 1));
turtleMover.registerMotion('→', turtle -> turtle.setX(turtle.getX() + 1));
// 原点上の亀
Turtle turtle = new Turtle(0, 0);
// 入力に従って亀を動かす
turtleMover.moveAround(turtle, userInput);
// 移動先を表示
System.out.printf("X=%d Y=%d%n", turtle.getX(), turtle.getY());
}
/** 平面上の亀. */
static class Turtle {
private int x, y;
Turtle(int x, int y) {
this.x = x;
this.y = y;
}
int getX() {
return this.x;
}
void setX(int x) {
this.x = x;
}
int getY() {
return this.y;
}
void setY(int y) {
this.y = y;
}
}
/** 亀の動きを起動するクラス. */
static class TurtleMover {
/** 文字と動作の対応. */
private final Map motionMap = new HashMap<>();
/** 文字に動作を割り当てる. */
void registerMotion(char ch, Motion motion) {
this.motionMap.put(ch, motion);
}
/** 入力文字列に従って亀を動かす. */
void moveAround(Turtle turtle, String input) {
for (char ch : input.toCharArray()) {
Motion motion = this.motionMap.get(ch);
if (motion != null) {
motion.move(turtle);
}
}
}
}
/** 亀の動作. ★関数的インタフェースであることを明示. */
@FunctionalInterface
interface Motion {
public void move(Turtle turtle);
}
}
実行結果は次のとおり、リスト6と同じです。
X=2 Y=1
リスト7でラムダ式を用いている箇所は、main メソッドの中で、各矢印文字と動作との対応を行う「矢印文字に対応する動作を登録」の部分です。TurtleMover クラスの registerMotion メソッドの第2引数として、turtle -> turtle.setX(turtle.getX() - 1) といったラムダ式を渡しています。registerMotion メソッドの第2引数は Motion インタフェース型として宣言されているので、これらのラムダ式は Motion インタフェース型のインスタンスを生成するのです。
リスト6では、LeftMotion クラスや DownMotion クラスなどを宣言して動作を記述する箇所と、コマンドオブジェクトを生成する箇所が離れていました。これに対してリスト7では、ラムダ式を用いることで、動作を記述するその場でコマンドオブジェクトを生成しています。より簡潔に記述できていることが分かると思います。

