BufferedReader in JavaFX

今回は前回の続きです。
前回 『String in JavaFX』で紹介した String の話で...

"" == null が 真 になる
というちょっと意外な仕様により、 以下の BufferedReader の典型的なコードが "意図しない動きをする" という問題を解決するために、『TranslatorFX』 で行った対応策を少し紹介したいと思います。この問題の詳細については String in JavaFX を参照してください。

var r = new java.io.BufferedReader(in);

var s: String;
while ((s = r.readLine()) != null) {
    println(s);
}

対応策と言ってもそんな対した話ではないのですが...
問題の起こりやすいコードは使わなければよいというだけです。 使わないと言っても本当に使わないという訳ではなく、"臭いものにはフタをしよう" と言うことで、 問題の起こりやすいコードは内部に隠蔽して、利用者側からは直接使わせないようにすればよいという意味です。

JavaFX の特徴の1つとして、関数型の変数という非常に便利なものがあります。 これは、Javascript のように 関数(オブジェクトへの参照) を変数に格納したり、別の関数の引数に渡したりできるというものです。(Java にも欲しいなぁ...)

であれば...
JavaFX の場合、以下のようなコードで対応するのが一番お似合いでしょう... (Prototype.js のパクリ?)
これなら、上記のコードより簡潔で間違うこともなく、更にクロージャも使えるのでとっても便利です。

FXReader { reader: in }.eachLine(function(line: String):Void {
    println(line);
});


ちなみに、上記のコードの FXReader は、以下のような感じです。

public class FXReader {

    public-init var reader: Reader;

    public function eachLine(func: function(String):Void) {

        var r = new BufferedReader(reader);

        try {
            var s: Object;
            while ((s = r.readLine()) != null) {
                func(s as String);
            }
        } finally {
            r.close();
        }
    }
}

やっていることは、とっても簡単で、ただ単に readLine() で1行づつ文字列を読込み、その文字列を引数 func で指定されたコールバック関数に渡しているだけです。おまけとして、このメソッド内でストリームのクローズ処理もしています。これは Groovy のパクリ...

実は、この解決策ですが、最初に対応したものはこれと少し違います。どこが変わったかというと...
readLine() で読み込んだ文字列を格納するための変数 s の型を String から Object に変更しました。

これは id:hide1080 さんの 『JavaFX の String 変数のデフォルト値は空文字列だけど null と等価扱いされる件(に物申す)』で紹介されていた 案4 をそのまま使わせてもらいました。(hide1080 さん、すごいです。)

元々は、Java 側で BufferedReader#readLine() をオーバーライドして、ストリームの終わりに達した際に null ではなく、EOF を表す文字列 (ASCIIコードの 0x1a) を返すようにするという駄作だったのです。明らかに hide1080 さんの 案4 の方がイケてます。