CUBは子供の白熊

Java SE 8 実践プログラミングの練習問題を解く

第6章 並行処理の機能強化 : 問題 11 : タスクを非同期に繰り返す

問題

次のメソッドを作成せよ

public static <T> CompletableFuture<T> repeat(Supplier<T> action, Predicate<T> until)

このメソッドuntil関数が受け入れる値を生成するまでactionを非同期に繰り返す
そして、until関数も非同期に実行する

解答

public static <T> CompletableFuture<T> repeat(Supplier<T> action, Predicate<T> until) {
    return CompletableFuture
    .supplyAsync(action)
    .thenComposeAsync(
        t -> {
            return until.test(t) ? CompletableFuture.completedFuture(t)
                                 : repeat(action, until);
        }
    );
}

再帰呼び出しを使うところが味噌
でも、こんなループ処理が遅延呼び出しで記述できるのは、すごい!

検証

以下の二つの関数

  • コンソールからPasswordAuthenticationを読み込む関数
  • 1秒間スリープすることで正当性検査をシミュレートし、パスワードが "secret" であるかどうかを検査する関数

を用いてテストせよ

解答

Scanner stdin = new Scanner(System.in);

CompletableFuture<PasswordAuthentication> f = repeat(
    // コンソールから PasswordAuthentication を読み込む
    () -> {
        System.out.print("User Name : ");
        String user = stdin.nextLine();
        System.out.print("Password : ");
        String password = stdin.nextLine();
        return new PasswordAuthentication(user, password.toCharArray());
    },
    // 正当性検査のシミュレーション
    auth -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            // とりあえず無視 :-)
        }
        return new String(auth.getPassword()).equals("secret");
    }
);
f.thenAccept(auth -> { System.out.println(auth.getUserName()); });
// タスクの処理が終わる前に終了しないようにするおまじない
ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.MINUTES);  // ちょっと長めに
System.out.println("終了");