読者です 読者をやめる 読者になる 読者になる

ジェネリクスでユーティリティ

Javaの理論と実践: 疑似typedefアンチパターン

ちょっと古めの記事だけど役に立つ内容なので紹介。「継承を使って疑似的にtypedefのようなものは実現できるけど、ぜんぜん駄目だから使うな」って話。で、それだけじゃなくて型推論を活用して、安全にタイプ量を減らす方法も載っていて、今回はそれをちょびっと発展させてみる。

public static <K, V> Map<K, V> newHashMap() {
    return new HashMap<K, V>();
}
安全なトリック

Javaでは戻り値が代入される変数の型によっても推論が働くから、これを使うと、

Map<String, Integer> m = newHashMap();

のように、型を一回指定するだけでいい。これはこれで便利だけど、同時に値も指定できたら便利だと思いません?
てことで実装例*1

public class CollectionCreator {
    private int coefficient;
    public CollectionCreator() {
        this(2);
    }
    public CollectionCreator(int coefficient) {
        this.coefficient = coefficient;
    }
    public final int getCoefficient() { return coefficient; }
    public void setCoefficient(int coefficient) {
        this.coefficient = coefficient;
    }
    
    public static final CollectionCreator DEFAULT =
        new CollectionCreator() {
            public void setCoefficient(int coefficient) {
                throw new UnsupportedOperationException();
            }
        };
    
    private <K, V, M extends Map<K, V>> M map(M map, Map.Entry<K, V>...entries) {
        for (Map.Entry<K, V> entry : entries) map.put(entry.getKey(), entry.getValue());
        return map;
    }
    
    private <E, C extends Collection<E>> C collection(C collection, E...elements) {
        for (E element : elements) collection.add(element);
        return collection;
    }
    
    public <K, V> HashMap<K, V> hashMal(Map.Entry<K, V>...entries) {
        return map(new HashMap<K, V>(entries.length * coefficient), entries);
    }
    
    public <K, V> TreeMap<K, V> treeMap(Map.Entry<K, V>...entries) {
        return map(new TreeMap<K, V>(), entries);
    }
    
    public <E> ArrayList<E> arrayList(E...elements) {
        return collection(new ArrayList<E>(elements.length * coefficient), elements);
    }
    
    public static <K, V> Pair<K, V> p(K key, V value) {
        return new Pair<K, V>(key, value);
    }
    public static final class Pair<K, V> implements Map.Entry<K, V> {
        private final K key;
        private final V value;
        public Pair(K key, V value) {
            this.key   = key;
            this.value = value;
        }
        public K getKey() { return key; }
        public V getValue() { return value; }
        public V setValue(V value) { throw new UnsupportedOperationException(); }
        @Override
        public boolean equals(Object other) {
            if (this == other) return true;
            if (other == null || !(other instanceof Pair)) return false;
            Pair p = (Pair) other;
            return (key == null ? p.key == null : key.equals(p.key)) &&
                (value == null ? p.value == null : value.equals(p.value));
        }
        @Override
        public int hashCode() {
            return (key == null ? 0 : key.hashCode()) ^
                (value == null ? 0 : value.hashCode());
        }
    }
}

ここでは3つしか載せてないけど、こんな感じでlinkedListやらhashSetやらを作っていくわけです。ただ、Mapを作るメソッドは呼び出し側で警告になってしまうのが問題といえば問題。あー、それにキャパシティは無視してコンストラクタをprivateに、全てのメソッドをstaticにしてしまうほうが良いかも*2 *3

それに、いちいち一個一個指定するのは面倒だし、もうちょっとヘルパメソッド作ってもいいかも。便利そうなのは全部が同じ要素な配列を作るメソッド、1ずつ増えてくor減ってく配列を作るメソッド*4、ランダムな要素をもつ配列を作るメソッドあたりか。

*1:本当はもっとごちゃごちゃした実装を書いたけど、面倒なので省略

*2:それかSingleton or MonoStateにするか

*3:つか、どうみても小さいプログラムに向けたものだから、全部staticが一番いいかもね

*4:Pythonのrange的なやつ