在Enum中声明List等可变集合是否是合理的开发实践?
问题:枚举中设置可变集合属于合理设计吗?
该枚举内部定义了一个List集合和对应的调用方法,调用该方法可向List中添加自定义对象,示例调用如下:SOURCE_MARKET_ORDER.createMergeDataByOrderType(hitValue, "ABC", "JKL");
枚举声明代码如下:
import com.google.common.collect.Lists; import java.util.List; import java.util.Map; public enum OrderDataType { SOURCE_ENTITY_ORDER("EntityMaster Bulk Edit", "EntityMaster Merge Source", "EntityMergeSource", "source"), TARGET_ENTITY_ORDER("EntityMaster Bulk Edit", "EntityMaster Merge Target", "EntityMergeTarget", "target"), SOURCE_INSTRUMENT_ORDER("InstrumentMaster Bulk Edit", "InstrumentMaster Merge Source", "InstrumentMergeSource", "source"), TARGET_INSTRUMENT_ORDER("InstrumentMaster Bulk Edit", "InstrumentMaster Merge Target", "InstrumentMergeTarget", "target"), SOURCE_MARKET_ORDER("MarketMaster Bulk Edit", "MarketMaster Merge Source", "MarketMergeSource", "source"), TARGET_MARKET_ORDER("MarketMaster Bulk Edit", "MarketMaster Merge Target", "MarketMergeTarget", "target"), XREF_ORDER("CustomXRef", "Merge Custom XRef", "CustomXRef", "xref"), ; private final String orderType; private final String orderTypeName; private final String componentName; private final String type; private final List<MergeData> mergeData = Lists.newArrayList(); OrderDataType(String orderType, String orderTypeName, String componentName, String type) { this.orderType = orderType; this.orderTypeName = orderTypeName; this.componentName = componentName; this.type = type; } public String getOrderType() { return orderType; } public String getOrderTypeName() { return orderTypeName; } public String getComponentName() { return componentName; } public String getType() { return type; } public List<MergeData> getMergeData() { return mergeData; } void createMergeDataByOrderType(Map<String, String> hitValue, String dataProductId, String xmlDocument) { mergeData.add(new MergeData(dataProductId, hitValue, xmlDocument )); } }
回答
这种设计完全不合理,属于对Java枚举特性的误用,会带来大量潜在隐患,具体问题如下:
- 违反枚举的核心设计定位:Java枚举的本质是全局固定的单例常量,设计初衷就是用来表示无状态的固定枚举项,所有人默认枚举实例是不可变的。在枚举中加入可变集合,相当于把全局共享的单例改成了有状态对象,完全违背了枚举的设计原则。
- 多线程安全隐患:枚举实例是全进程共享的,多个线程同时调用
createMergeDataByOrderType添加数据、或者读取mergeData时,会直接触发并发修改异常、数据丢失、数据错乱等问题,这类问题通常非常隐蔽,排查成本极高。 - 内存泄漏风险:枚举实例的生命周期和JVM进程完全绑定,只要程序不停止,你往
mergeData里添加的所有对象都不会被GC回收,数据积累到一定程度就会触发OOM。 - 维护性极差:其他开发者在调用枚举时,默认不会考虑枚举会被修改,很容易在不知情的情况下写出依赖枚举状态的bug代码,后续迭代维护的风险极高。
优化方案
如果要实现类似的业务需求,可以采用以下更合理的实现方式:
- 状态和枚举解耦:单独定义业务上下文对象,或者使用
ThreadLocal存储不同枚举项对应的MergeData集合,不要把可变状态放到枚举本身中。 - 禁用修改:如果
mergeData是固定初始化的配置数据,在构造函数中完成填充后,直接用Collections.unmodifiableList把它包装成不可变集合,同时删除createMergeDataByOrderType方法,禁止外部修改。 - 每次返回新集合:如果确实需要基于枚举生成动态数据,修改方法逻辑,每次调用都返回新创建的集合实例,不要修改枚举内部的属性。
内容的提问来源于stack exchange,提问作者Jagdish Aswani
相关产品推荐
相关产品推荐

