Map型のインスタンス変数やクラス変数に”格好良く”初期値を与える方法(1)

Pocket

次のようなクラスがあるとします。

import java.util.HashMap;
import java.util.Map;

class Class {
    Map instanceMap = new HashMap();
    static Map staticMap = new HashMap();
}

もし、インスタンス変数instanceMapやクラス変数staticMapに”格好良く”初期値を与えたいとします。
さて、どうしましょう。

こういう方法があります。

import java.util.HashMap;
import java.util.Map;

class Class {
    Map instanceMap = new HashMap() {{put("key1","value1"); put("key2","value2");}};
    static Map staticMap = new HashMap() {{put("key3","value3"); put("key4","value4");}};
}

一目見ただけだと何をやっているかわかりませんね。何をやっているかわからないあたりが、秘密の組織っぽくて格好良いです。という事で、格好良く初期値を与えることができました。めでたしめでたし。

・・・いや、もうちょっと深掘りしてみましょう。

まずは、何をやっているのかを考えてみます。
最初にお見せしたコードをインスタンス変数だけにします。

class Class {
    Map instanceMap = new HashMap() {{put("key1","value1"); put("key2","value2");}};
}

上のコードから、{put(“key1″,”value1”); put(“key2″,”value2”);}を取り除きます。
すると、こうなります。

class Class {
    Map instanceMap = new HashMap() {};
}

なんか微妙に気持ち悪いです。
気持ち悪いのは、後ろに空っぽの{}ブロックがあるせいです。

これにどんな意味があるのかと言うと、実装のない匿名クラスを定義している事になります。
(匿名クラスがわからない方はググってください)

これと等価なコードは、次のコードです。

class AnonymousHashMap extends HashMap {
}
class Class {
    Map instanceMap = new AnonymousHashMap();
}

この例ではAnonymousHashMapというHashMapを継承したクラスを定義していますが、わざわざ名前を付けて定義しなくても処理中にて新しいサブクラスを生成できるのが匿名クラスと呼ばれるものです。インターフェースを実装したクラスを作るのが面倒な時などに便利です。
・・・あ、匿名クラスがわからない人はググってと言いながら、勢い余って説明してしまいました。

これに、さっき取り除いた{put(“key1″,”value1”); put(“key2″,”value2”);}を入れると、次のようになります。(わかりやすいように整形)

class AnonymousHashMap extends HashMap {
    {
        put("key1","value1");
        put("key2","value2");
    }
}
class Class {
    Map instanceMap = new AnonymousHashMap();
}

次に気持ち悪いのがputメソッドを囲んでいる、{}ブロックです。
これは、インスタンスイニシャライザと呼ばれるもので、コンストラクタの前に呼ばれます。

ここまで読めば、最初のソースコードの意味が分かるのではないでしょうか。

class Class {
    Map instanceMap = new HashMap() {{put("key1","value1"); put("key2","value2");}};
    static Map staticMap = new HashMap() {{put("key3","value3"); put("key4","value4");}};
}

これは、次のソースコードを短く書いたものという事になります。

class AnonymousHashMap1 extends HashMap {
    {
        put("key1","value1");
        put("key2","value2");
    }
}
class AnonymousHashMap2 extends HashMap {
    {
        put("key3","value3");
        put("key4","value4");
    }
}
class Class {
    Map instanceMap = new AnonymousHashMap1();
    static Map staticMap = new AnonymousHashMap2();
}

ようやく本題です。
今回の本題は「Map型のインスタンス変数やクラス変数に”格好良く”初期値を与える方法」です。

はたしてこれは格好良いでしょうか。

class Class {
    Map instanceMap = new HashMap() {{put("key1","value1"); put("key2","value2");}};
    static Map staticMap = new HashMap() {{put("key3","value3"); put("key4","value4");}};
}

この方法の欠点は、上でいくつか挙げたような気持ち悪さがあることです。そして、私が思うこの方法の一番の格好悪さは、特に必要のないのに匿名クラスを使っている事です。匿名クラスを使った場合の嫌な所は、余計なclassファイルが作られる事です。

私はこの ~$1.class、~$2.classというファイルが好きではないのです。匿名クラスを使うべき所1で余計なclassファイルが作られるのはしょうがないのですが、今回の場合は違う気がするのです。

もっとかっこいい方法

では、私が思う格好いい方法をお教えしましょう。
次の通りです。

import java.util.HashMap;
import java.util.Map;

class Class {
    Map instanceMap = new HashMap();
    static Map staticMap = new HashMap();

    /**
     * staticイニシャライザ。
     */
    static {
        staticMap.put("key3","value3");
        staticMap.put("key4","value4");
    }

    /**
     * コンストラクタ。
     */
    Class() {
        instanceMap.put("key1","value1");
        instanceMap.put("key2","value2");
    }
}

・・・ごめんなさい。普通です。
でも普通で何が悪い。

欠点を挙げると、コンストラクタのオーバーロードを行った場合に引数なしコンストラクタの呼び出しを書き忘れるとインスタンス変数の初期化が行われません。

import java.util.HashMap;
import java.util.Map;

class Class {
    Map instanceMap = new HashMap();
    static Map staticMap = new HashMap();

    /**
     * staticイニシャライザ。
     */
    static {
        staticMap.put("key3","value3");
        staticMap.put("key4","value4");
    }

    /**
     * コンストラクタ。
     */
    Class() {
        instanceMap.put("key1","value1");
        instanceMap.put("key2","value2");
    }

    /**
     * 引数ありのコンストラクタ。
     */
    Class(int i) {
        // this()を書き忘れるとバグになる
    }
}

では、どう解決するか。
前述のインスタンスイニシャライザを使います。

import java.util.HashMap;
import java.util.Map;

class Class {
    static Map staticMap = new HashMap();

    /**
     * staticイニシャライザ。
     */
    static {
        staticMap.put("key3","value3");
        staticMap.put("key4","value4");
    }

    Map instanceMap = new HashMap();

    /**
     * インスタンスイニシャライザ。
     */
    {
        instanceMap.put("key1","value1");
        instanceMap.put("key2","value2");
    }

    /**
     * 引数ありのコンストラクタ。
     */
    Class(int i) {
        // インスタンスイニシャライザは必ず呼ばれる
    }
}

これが一番格好良いでしょうか。
いや、もっといい方法があるはずです。

・・・長くなってしまったので今回はこの辺で。

【追記】
続き書きました。
Map型のインスタンス変数やクラス変数に”格好良く”初期値を与える方法(2)

Pocket

  1. ComparatorやListenerやRunnableあたりでしょうか。 []

コメントを残す

メールアドレスが公開されることはありません。