LoginSignup
2
2

More than 5 years have passed since last update.

[java] MapBuilder

Posted at

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はないものでしょうか?

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2