为什么私有静态最终列表/集/地图不可修改?

时间:2012-12-10 01:23:42

标签: java collections map constants immutability

我刚读了一些经验丰富的程序员编写的代码,我发现了以下内容:

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初始化为HashMap,然后直接初始化put键值对,而无需创建虚拟占位符映射或使字段成为不可变的地图。我在这里错过了什么吗?

6 个答案:

答案 0 :(得分:9)

如果您不小心return PREFIXES某个方法,那么突然之外的任何其他代码都可以修改它。当你在将来凌晨3点修改代码时,使常量真正不变是可以防止你自己的愚蠢。

答案 1 :(得分:9)

通过使列表不可修改,作者记录了他的假设,即值永远不会改变。谁可能在以后编辑该课程,不仅可以看到这个假设,而且还会被提醒以防它被打破。

这只有在采取长期观点时才有意义。它降低了因维护而产生新问题的风险。我喜欢这种编程风格,因为即使在我自己的课程中,我也倾向于破坏它。有一天,你可能会进行快速修复而忘记最初做出的假设并且与正确性相关。你可以越多地锁定代码,就越好。

答案 2 :(得分:3)

使用private地图,集合或数组可以从类外部修改,这非常容易。你要标记它final,为什么不会说明它也应该是不可变的呢?

答案 3 :(得分:3)

假设你的朋友离开了他的工作,一个经验不足的程序员接管了。经验较少的程序员尝试在同一类中的不同方法中修改PREFIXES的内容。它是不可修改的,它将无法工作。这是说“这是一个常数,不要改变它。”的正确方法。

答案 4 :(得分:1)

Map接口不会传达您希望某些内容不可变或不可修改的内容。

以下方法适用于Eclipse Collections(以前的GS 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();

这将创建一个合约不可变的Map,因为ImmutableMap在其API中没有变异方法。

如果您希望保留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 Collections的提交者。

答案 5 :(得分:0)

如果集合是最终的,则无法在其中设置新对象。 但是,仍然可以向同一对象添加或删除项目。

当您使其无法修改时,您甚至无法在集合中添加或删除项目。 因此,总是建议使集合不可修改,而不是仅仅保持最终。