JavaでもRubyみたいなMap(Hash)のリテラルが欲しい!

と思って考えてみました。

Javaで適当なMap作るときって、

Map<String, String> map = new HashMap<String, String>();
map.put("tako", "octopus");
map.put("ika", "squid");
map.put("namako", "sea cucumber");

ってな感じじゃないですか?

RubyとかだとMap(RubyだとHash)のリテラルがあるんで、

hash = {"tako" => "octopus", "ika" => "squid", "namako" => "sea cucumber"}

と書けて楽です。
ScalaにもOGNLにもこんな感じの記法があるので是非Javaにもって思うんですが、ないんですよね…。

で、この前もうちょっと楽に書けないかと思って、こんなクラスを作ってみました。

public final class ColUtil {

	public static class FluentMap<K, V> extends LinkedHashMap<K, V> {

		public FluentMap<K, V> p(K key, V value) {
			this.put(key, value);
			return this;
		}
	}

	public static FluentMap<Object, Object> $m(Object key, Object value) {
		return new FluentMap<Object, Object>().p(key, value);
	}

	public static FluentMap<String, Object> $m_so(String key, Object value) {
		return new FluentMap<String, Object>().p(key, value);
	}

	public static FluentMap<String, String> $m_ss(String key, String value) {
		return new FluentMap<String, String>().p(key, value);
	}
}

使い方はこんな感じ。

import static com.tetz42.util.ColUtil.*;
      :
	// ↑と同じ例
	Map<String, String> map = $m_ss("tako", "octopus").p("ika", "squid")
		.p("namako", "sea cucumber");

	// その他のメソッドの使用例
	Map<Object, Object> map1 = $m("key1", "value1").p(0, 99);
	Map<String, Object> map2 = $m_so("One", 1).p("Two", 2)
		.p("Threee", new RuntimeException());
      :

リテラルっぽさを出したかったので、各staticメソッドの先頭に「$」つけてます。
なお、FluentMapをLinkedHashMapから継承させてるのは、書いた順番を保持したいケースもありそうに思ったからです。
OGNLもデフォルトでLinkedHashMapらしいですし。

Map版のほかにList版とSet版も作ったんですが、予想つくと思うんでソースは省略します。

Listの要素がMapになっている例

Rubyだと、

list = [
  {"name" => "John",  "sex" => "male"},
  {"name" => "Linda", "sex" => "female"},
  {"name" => "Taro",  "sex" => "2 times per week"} ]

と書けます。

で、今回のColUtilを使うとこんな感じです。

import static com.tetz42.util.ColUtil.*;
import com.tetz42.util.ColUtil.FluentList;
      :
	// このメソッドを作成する必要あり!
	public static FluentList<Map<String, String>> $lm(Map<String, String> map) {
		return new FluentList<Map<String, String>>().a(map);
	}
      :
		List<Map<String, String>> list = 
			$lm($m_ss("name", "John" ).p("sex", "male"))
			 .a($m_ss("name", "Linda").p("sex", "female"))
			 .a($m_ss("name", "Taro" ).p("sex", "2 times per week"));
      :

まとめ

うーん、多少は似せれたんじゃないかとは思うんですけど、やっぱ元からリテラルが存在している言語と比べると見劣りしますね…。
それでも、最後の例を通常どおりのJavaの書き方した場合の長さを考えるとそれなりに有用な気がしてるんですけど、どうでしょうかね?