MapBuilderと呼ばれるものはいろいろあるわけですが、なにかこうしっくりこないのです。
右側に無駄な型パラメータを書かなければならなかったりするじゃないですか?
Map<A,B> map = new MapBuilder<A,B>()...
の右側のA,Bが無駄な気がして嫌なのです。
そこで少し考えてみました。
public class MapBuilderTest {
Map<String,Integer> map = MapBuilder.createHashMap()
.$_("A" , 1)
.$_("B" , 2)
.$_("C" , 3)
.buildUnmodifiable();
Map<Month,Integer> enumMap = MapBuilder.createEnumMap()
.$_(Month.JANUARY , 1)
.$_(Month.FEBRUARY , 2)
.$_(Month.MARCH , 3)
.build();
Map<String,String> map2 = createHashMap(
$("A","a"),
$("B","b"),
$("C","c")
);
上二つはいわゆるfluent interfaceをもちいたもの(type A)、下はEntry配列を用いたもの(type B)です。
使い勝手としてどちらがいいのでしょうかね。
実装
こんな感じです。
###type A
public class MapBuilder<K,V> {
private Map<K,V> map;
public static HashMapSeed createHashMap(){
return new HashMapSeed();
}
public static EnumMapSeed createEnumMap(){
return new EnumMapSeed();
}
private MapBuilder(Map<K,V> map){
this.map = map;
}
public MapBuilder<K,V> $_(K k,V v){
map.put(k,v);
return this;
}
public Map<K,V> buildUnmodifiable(){
return Collections.unmodifiableMap(map);
}
public Map<K,V> build(){
return map;
}
public static class HashMapSeed{
public <K,V> MapBuilder<K,V> $_(K k,V v){
MapBuilder<K,V> builder = new MapBuilder<K,V>(new HashMap<K,V>());
return builder.$_(k,v);
}
}
public static class EnumMapSeed{
public <K extends Enum<K>,V> MapBuilder<K,V> $_(K k,V v){
MapBuilder<K,V> builder = new MapBuilder<>(new EnumMap<>(k.getClass()));
return builder.$_(k,v);
}
}
}
###type B
public class MapBuilder<K,V> {
public static class Entry<K,V>{
public K k;
public V v;
public Entry(K k,V v){
this.k = k;
this.v = v;
}
}
public static <K,V> Entry<K,V> $(K k, V v){
return new Entry<>(k,v);
}
public static <K,V> Map<K,V> createHashMap(Entry<? extends K,? extends V>...entries){
Map<K,V> map = new HashMap<>();
for(Entry<? extends K,? extends V> e : entries){
map.put(e.k, e.v);
}
return map;
}
}
type BはStatic Importしないと使いづらいですが、それができる現場ばかりではないだろうというのが問題ですが、きちんと左側のMap<K,V>
が右側にも生きている事は利点ですかね。
type Aは右側のパラメータと左側のパラメータが切り離されてしまっています。fluent interfaceを使う以上は左右の型の接続関係は最後のbuild()まで分からないわけですから、これは仕方ありませんよね。
- type Aの型は、最初の
$_(K k,V v)
メソッドに与えられるk,vの型で確定されてしまう。 - type Bの型は、左側の受け手の型がきちんと生かされる。
MapBuilder一つでも悩んでしまう今日この頃です。なにか良いMapBuilderはないものでしょうか?