もっと Lambda その13

今日のエントリーも「もっと Lambda」シリーズです。

今回はよく使われるデザインパターンで知られている Factory Method pattern を Java8 を使ってコーディングしてみます。

ちなみにこのネタは海外のサイトで紹介されていたものです。

それを少し変えて写経しただけですが随分シンプルになるので感激です。

それでは現在の Java だと下記のようになります。

package jp.yucchi.factorymethodpattern4oldjava;

public interface Airplane {

    public void pilot();

    public void maintain();
}

package jp.yucchi.factorymethodpattern4oldjava;

public class JetAirplane implements Airplane {

    @Override
    public void pilot() {
        System.out.println("ジェット機を操縦します。");
    }

    @Override
    public void maintain() {
        System.out.println("ジェット機を整備します。");
    }
}

package jp.yucchi.factorymethodpattern4oldjava;

public class FighterPlane implements Airplane {

    @Override
    public void pilot() {
        System.out.println("戦闘機を操縦します。");
    }

    @Override
    public void maintain() {
        System.out.println("戦闘機を整備します。");
    }
}

package jp.yucchi.factorymethodpattern4oldjava;

abstract class AirplanePilot {

    public abstract Airplane getAirplane();

    public void pilotAirplane() {
        getAirplane().pilot();
    }

    public void maintainAirplane() {
        getAirplane().maintain();
    }
}

class JetAirplanePilot extends AirplanePilot {

    @Override
    public Airplane getAirplane() {
        return new JetAirplane();
    }
}

class FighterPlanePilot extends AirplanePilot {

    @Override
    public Airplane getAirplane() {
        return new FighterPlane();
    }
}

package jp.yucchi.factorymethodpattern4oldjava;

public class FactoryMethodPattern4OldJava {

    public static void main(String[] args) {
        pilotAirplane(new JetAirplanePilot());
        pilotAirplane(new FighterPlanePilot());
    }

    private static void pilotAirplane(AirplanePilot aPilot) {
        System.out.println("I pilot a new airplane.");
        aPilot.pilotAirplane();
        aPilot.maintainAirplane();
    }
}

Factory Method pattern でよく見かける典型的なサンプルですね。

インスタンスの生成をサブクラスに任せることによって柔軟に生成するインスタンスを選択することを可能としています。

では Java8 ではどのようなコードになるのでしょうか?

package jp.yucchi.factorymethodpattern4lambda;

public interface Airplane {

    public void pilot();

    public void maintain();
}

package jp.yucchi.factorymethodpattern4lambda;

public class JetAirplane implements Airplane {

    @Override
    public void pilot() {
        System.out.println("ジェット機を操縦します。");
    }

    @Override
    public void maintain() {
        System.out.println("ジェット機を整備します。");
    }
}

package jp.yucchi.factorymethodpattern4lambda;

public class FighterPlane implements Airplane {

    @Override
    public void pilot() {
        System.out.println("戦闘機を操縦します。");
    }

    @Override
    public void maintain() {
        System.out.println("戦闘機を整備します。");
    }
}

package jp.yucchi.factorymethodpattern4lambda;

public interface AirplanePilot {

    public Airplane getAirplane();

    public default void pilotAirplane() {
        getAirplane().pilot();
    }

    public default void maintainAirplane() {
        getAirplane().maintain();
    }
}

package jp.yucchi.factorymethodpattern4lambda;

public class FactoryMethodPattern4Lambda {

    public static void main(String[] args) {
        pilotAirplane(JetAirplane::new);
        pilotAirplane(FighterPlane::new);
    }

    static void pilotAirplane(AirplanePilot aPilot) {
        System.out.println("I pilot a new airplane.");
        aPilot.pilotAirplane();
        aPilot.maintainAirplane();
    }
}

Java8 では抽象化クラス AirplanePilot とそれの具象化クラス二つが必要とされません。

その代わりに、Java8 の新機能の Default methods ( Virtual extension methods ) を実装したインターフェイスを利用します。

実行結果はどちらも次のようになります。

I pilot a new airplane.
ジェット機を操縦します。
ジェット機を整備します。
I pilot a new airplane.
戦闘機を操縦します。
戦闘機を整備します

実にシンプルで綺麗なコードで Factory Method pattern が実装できてしまうんですね!

Hatena タグ:

もっと Lambda その12

昨日の Java パズラーの超ウルトラヒントを・・・

たぶん、必要ないと思うけど・・・

package jp.yucchi.infinity;

import java.util.stream.IntStream;

public class Infinity {

    public static void main(String[] args) {

        IntStream.range(Integer.MAX_VALUE - 3, Integer.MAX_VALUE, 2).limit(5).forEach(System.out::println);

        System.out.println("***********************************");
        
        int count = 0;
        for (int i = 0; i <= Integer.MAX_VALUE; Math.addExact(0, i++)) {
            count++;
            if (i > Integer.MAX_VALUE - 5) {
                System.out.println(i);
            }
        }
        System.out.println(count);
    }

}

 

このパズルは古典的なオーバーフローをとりあげてます。

IntStream.range(Integer.MAX_VALUE - 3, Integer.MAX_VALUE, 2)

この新しい構文は、

for (int i = startInclusive; i < endExclusive ; i += step) { ... }

と同じで、i += step のところでオーバーフローをおこします。

 

for (int i = 0; i <= Integer.MAX_VALUE; Math.addExact(0, i++)) { ... }

このループ文は一見すると Math.addExact() メソッドを利用しているので例外が出ると思ってしまいがちですが、

よ~~~く見ると引数の i++ でオーバーフローするので無限ループになってしまいます。

とても古典的で簡単なパズルでした。

これだけだと寂しいので Math.addExact() メソッドを覗いてみました。

    public static int addExact(int x, int y) {
        int r = x + y;
        // HD 2-12 Overflow iff both arguments have the opposite sign of the result
        if (((x ^ r) & (y ^ r)) < 0) {
            throw new ArithmeticException("integer overflow");
        }
        return r;
    }

なんか良く解らないけどビット演算を駆使して判定しているようですね。

なんで今頃こんなものを・・・以下自粛。(^_^;)

Hatena タグ: