10

より経験豊富なプログラマーによって書かれたコードを読んだところ、次のことに出くわしました。

public class ConsoleFormatter extends Formatter {
    private static final Map<Level, String> PREFIXES;

    static {
        Map<Level, String> prefixes = new HashMap<Level, String>();
        prefixes.put(Level.CONFIG,  "[config]");
        prefixes.put(Level.FINE,    "[debug]");
        prefixes.put(Level.FINER,   "[debug]");
        prefixes.put(Level.FINEST,  "[trace]");
        prefixes.put(Level.INFO,    "[info]");
        prefixes.put(Level.SEVERE,  "[error]");
        prefixes.put(Level.WARNING, "[warning]");

        PREFIXES = Collections.unmodifiableMap(prefixes);
    }

    // ...

}

ご覧のとおり、これはログ出力の書式設定に使用されるクラスです。しかし、私の目を引いたのは、静的初期化ブロックのコードでした: PREFIXES = Collections.unmodifiableMap(prefixes);.

なぜPREFIXES変更不可能なマップになったのですか? これはプライベート定数であるため、そのクラスの外部でデータを変更するリスクはありません。定数の不変性に完全感を与えるために行われたのでしょうか?

個人的には、ダミーのプレースホルダー マップを作成したり、フィールドを不変のマップにしたりせずに、 として直接初期化し、次にキーと値のペアを直接入力しPREFIXESますHashMapputここで何か不足していますか?

4

6 に答える 6

9

誤っreturn PREFIXESてメソッドから取得した場合、突然、そこにある他のコードがそれを変更できます。定数を真に不変にすることで、将来午前 3 時にそのコードを変更するときに、自分の愚かさを防ぐことができます。

于 2012-12-10T02:55:59.493 に答える
9

リストを変更不可能にすることで、著者は、値が決して変更されないという彼の仮定を文書化しました。後でそのクラスを編集する可能性のある人は誰でも、その仮定を見ることができるだけでなく、それが壊れた場合に備えて思い出させることもできます.

これは、長期的な視点でのみ意味があります。メンテナンスによって新たな問題が発生するリスクを軽減します。私はこのスタイルのプログラミングをするのが好きです。なぜなら、私は自分のクラスでさえ何かを壊す傾向があるからです。ある日、簡単な修正を求めて、最初に作成された正確性に関連する仮定を忘れてしまうかもしれません。コードをロックダウンできるほど、より良い結果が得られます。

于 2012-12-10T03:02:26.130 に答える
3

privateクラス外から変更可能なマップ、コレクション、または配列を持つことは驚くほど簡単です。あなたはそれfinalをマークしますが、それが不変であるはずであることも綴らないのはなぜですか?

于 2012-12-10T01:27:08.443 に答える
3

あなたの友人が仕事を辞め、経験の浅いプログラマーが引き継いだとします。経験の浅いプログラマーは、同じクラス内の別のメソッドのどこかで PREFIXES の内容を変更しようとします。それは変更不可能であり、機能しません。「これは定数です。変更しないでください」と言うのが適切な方法です。

于 2012-12-10T01:36:50.987 に答える
1

Map インターフェースは、何かを不変または変更不可にしたいということを伝えません。

次のアプローチは、Eclipse Collectionsで機能します。

private static final ImmutableMap<Level, String> PREFIXES = UnifiedMap.<Level, String>newMap()
    .withKeyValue(Level.CONFIG, "[config]")
    .withKeyValue(Level.FINE, "[debug]")
    .withKeyValue(Level.FINER, "[debug]")
    .withKeyValue(Level.FINEST, "[trace]")
    .withKeyValue(Level.INFO, "[info]")
    .withKeyValue(Level.SEVERE, "[error]")
    .withKeyValue(Level.WARNING, "[warning]")
    .toImmutable();

ImmutableMap の API には変更メソッドがないため、これにより、契約上不変の Map が作成されます。

Map インターフェースを維持したい場合は、この方法も有効です。

private static final Map<Level, String> PREFIXES = UnifiedMap.<Level, String>newMap()
    .withKeyValue(Level.CONFIG, "[config]")
    .withKeyValue(Level.FINE, "[debug]")
    .withKeyValue(Level.FINER, "[debug]")
    .withKeyValue(Level.FINEST, "[trace]")
    .withKeyValue(Level.INFO, "[info]")
    .withKeyValue(Level.SEVERE, "[error]")
    .withKeyValue(Level.WARNING, "[warning]")
    .asUnmodifiable();

どちらの場合も静的ブロックは必要ないことに注意してください。

注: 私は Eclipse コレクションのコミッターです。

于 2012-12-10T17:57:03.823 に答える