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 の方がイケてます。