習作:Minestra - Java8以降対応のユーティリティーライブラリー #java8
Java8未満を切り捨てて色々と作ってみるシリーズ。
Releases · argius/minestra - GitHub
Java8以降向けのユーティリティー集ライブラリーです。
名前は "Minestra" 。イタリア語でスープの意味です。名前には特に意味はありません。
今のところ、機能は以下の3つです。
ImmArray
- 不変配列+操作の強化 (Stream
に類似?)PathIterator
- findイテレーター(Files.find
に類似)I18nResource
- I18N (ResourceBundle
に類似)
類似機能ばかりですが、それには一応、理由があります。
わざわざ書き換えなくても、こうすれば上手くできるよ!、のようなのがありましたら、是非教えてください。よろしくお願いします。
ImmArray
- 不変配列
Java8はラムダ式と共にStream
が使えるようになって、すごく便利になりましたね。しかし、残念ながらメソッドが足りないと思います。仕組み上の制約なんでしょうか。
また、従来のコレクションは、インターフェイスの性質が不変でないので、不変を前提としたコードが書きづらいです。
それなら、Functional Java などの関数型向けライブラリーを使えば良いのでは? とおっしゃるかもしれませんが、あれは言語の拡張に近いというかレイヤーが厚すぎると思うのです。配列に対してそのままmap
関数が使えれば良いだけなんです。
そこで、配列をラップして関数型っぽいメソッドを持たせたものを作ってみました。それがImmArray
です。名前は、Javaらしいシンプルで一般的な名前にすると被りそうなので、それっぽくない名前にしてみました。
実は既にSeq-for-Java8というのを作っていて、それの焼き直しです。
ImmArray
のコード例
// import java.util.*; // import minestra.collection.*; ImmArray<String> a = ImmArray.of(Arrays.asList("1"), Optional.of("2"), ImmArray.of("3", "A"), Optional.empty(), Arrays.asList("5"), "B", Stream.of("6", "7")).flatten(); // => [1, 2, 3, A, 5, B, 6, 7]
実装には、ラムダ式、インターフェイスのdefaultメソッドとstaticメソッドを大量に使っています。
Stream
と同じように、IntXXX
,LongXXX
,DoubleXXX
とセットになっています。
配列をラップしているだけなので、スケーラビリティーやパフォーマンスが求められる場面には向いていません。シビアじゃない状況で簡潔に書きたいときに使ってください。
これでもまだメソッドが足りないくらいですね。でも、タプルが無いJavaでは素直に表現できないものがあったりするので、今のところは作っていません。例えばzip
とか。他にも、permutation
は迷うところです。
PathIterator
- findするIteratorもしくはStream
Stream
に合わせて、NIO.2のFiles
クラスにfind
メソッドが追加されました。
このメソッドは非常に便利なのですが、途中でIOException
が発生すると、そこで検索を終了してしまうんです。警告を出して検索を続行する、のようにはできません。
PathIterator
は、エラーの場合でも処理を続行できるようにしています。
PathIterator
のコード例
// import java.nio.file.*; // import minestra.file.*; // /tmp以下の100KB以上のファイルパスをprint PathIterator.streamOf(Paths.get("/tmp")).filter(x -> x.toFile().length() >= 1024L * 100) .forEach(System.out::println); // Streamに変換しなくても使える for (Path x : PathIterator.of(Paths.get("/tmp"))) { System.out.println(x); }
これもパフォーマンスの点では本物には敵わないと思いますので、そうでない場合に使ってください。
今後は、エラーハンドラーをラムダ式で指定できるように拡張を考えています。
実はこの名前は標準APIのとあるクラスと名前が被っているのですが、たぶん一緒に使うことは無いので大丈夫でしょう。
なお、このクラスはPotaufeuから単独で使えるように抽出したものです。
I18nResource
- I18Nテキストリソース
ResourceBundle
は、デフォルトロケールに対応するプロパティーファイルが用意されていない場合の動作に不満があります。
サブクラスを作れば対応できるのだと思いますが、個人的にはファイルだけでタライ回しのようなことがしたいのです。
I18nResource
は、親のリソースを決めておき、指定したリソースが無い場合は祖先をたどることができるようにしたものです。
また、テキストファイルはUTF-8で書きます。拡張子は.txt
にしています。native2asciiで書いたものは使えません。
PathIterator
のコード例
// import java.util.*; // import minestra.resource.*; // /default.txt I18nResource rootBase = I18nResource.create(Locale.JAPAN); // /yourpkg/default.txt I18nResource pkgBase = I18nResource.create("/yourpkg/", Locale.JAPAN); // /yourpkg/YourClass.txt I18nResource res = rootBase.derive(YourClass.class); // /yourpkg/YourClass_ja.txt I18nResource resJa = rootBase.derive(YourClass.class, Locale.JAPAN); String s = res.string("key1"); int i = res.integer("key2"); boolean b = res.isTrue("key3");
そういえば、Java9ではプロパティーファイルのUTF-8対応があるみたいですね(=>JEP 226: UTF-8 Property Files)。それと互換性を持たせられると良いんですが。
なお、このI18nResource
は、Stew4に含まれているResourceManager
を独立させて機能強化したものです。
まだアルファ版なので大幅に変更するかも知れません。
新しい機能を思いついたら追加しようと思います。
あわよくば、Mavenに登録したりも考えています。
(おわり)