我刚读了一些经验丰富的程序员编写的代码,我发现了以下内容:
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
键值对,而无需创建虚拟占位符映射或使字段成为不可变的地图。我在这里错过了什么吗?
答案 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)
如果集合是最终的,则无法在其中设置新对象。 但是,仍然可以向同一对象添加或删除项目。
当您使其无法修改时,您甚至无法在集合中添加或删除项目。 因此,总是建议使集合不可修改,而不是仅仅保持最终。