Semicolonless Java8

JJUG CCC 2013 Fallの懇親会のLT(ライトニングトーク)でSemicolonless Java8について発表しました。

Semicolonless Java とはエクストリームプログラミング(要するにプログラミングの縛りプレイ)でルールは以下のとおりです。

Java7版についてはJavaOne Tokyo 2012のLT解説が詳しいのでそちらを参照してください。
または 公式wikiを参照してください。

Semicolonless Java7までは戻り値のないメソッドの呼び出しにはリフレクションを用いる必要がありました。

Semicolonless JavaからJavaAPIを呼び出す場合の基本書式は以下の通りです。Java言語のリフレクションが用いられます。

if (null == インスタンスの型.class.getMethod("メソッド名", 引数の型.class, 引数の型.class, ... )
.invoke(インスタンス, 引数, 引数, ... )){}

http://wiki.livedoor.jp/semicolonlessjava/d/Hello%20world

Semicolonless Java8 ではラムダを用いることでリフレクションから解放されます。

if (null == java.util.concurrent.Executors.callable(() -> System.out.println("Hello world")).call()){}

この動きですが、Executors.callable()の引数はjava.lang.Runnable型で、ラムダの

() -> System.out.println("Hello world")

はRunnable型と解釈されます。ですからこのprintln()はRunnable.run()の実装としてふるまいます。

ただし、Runnable.run()は戻り値がvoid型でそのままではセミコロンレスに呼び出すことができません。ScheduledThreadPoolExecutor.submit()にRunnableを渡すのであれば戻り値があるのでセミコロンレスに呼び出し可能ですが、非同期実行されても困りますよね。

そこでExecutorsのcallable()を利用することにします。このユーティリティによって戻り値がvoid型のRunnable.run()は戻り値のあるCallable.call()へと変換されました。

あとはいつものようにif文で囲ってnull値と比較すればokです。

リフレクションフリーになったのでリファクタリングでもメソッド名の変更が追従できますよ。

JavaFXHello World デモのソースは以下の通り

public class Hello extends javafx.application.Application{

    @Override
    public void start(javafx.stage.Stage stage) throws Exception {
        if (java.util.concurrent.Executors.callable(() -> stage.setTitle("Hello, World!")).call() == null){}
        for (javafx.scene.layout.AnchorPane root :
                new javafx.scene.layout.AnchorPane[]{new javafx.scene.layout.AnchorPane()}) {
            if (root.getChildren().add(new javafx.scene.control.Label("Hello, World!"))){}

            if (java.util.concurrent.Executors.callable(() -> stage.setScene(
                    new javafx.scene.Scene(root, 300, 120))).call() == null){}
            if (java.util.concurrent.Executors.callable(() -> stage.show()).call() == null){}
        }
    }

    public static void main(String... args) throws Exception {
        if (java.util.concurrent.Executors.callable(() -> launch(args)).call() == null){}
    }
}