MVC模式下HashMap拷贝后内部对象可修改的问题求助
这个问题其实是典型的浅拷贝 vs 深拷贝的坑——你现在只拷贝了HashMap本身,但里面的键(AbstractArtistry)和值(ArrayList<AbstractCommand>)都是原对象的引用,所以控制器拿到副本后,修改这些引用指向的对象,自然会影响模型里的原始数据。下面给你几个可行的解决方案,按推荐程度排序:
AbstractArtistry设计为不可变类 这是最推荐的方案,从根源上杜绝被修改的可能。不可变类的核心逻辑是:对象一旦创建,内部状态就永远不能被改变。设计要点如下:
- 所有成员变量用
private final修饰,确保无法被重新赋值 - 不提供任何修改成员变量的
setter方法 - 构造方法一次性初始化所有成员变量,对引用类型字段做防御性拷贝
- 禁止子类重写方法(可以把类设为
final,或者把所有方法设为final)
举个AbstractArtistry的实现例子:
import java.util.Objects; public final class AbstractArtistry { private final String artName; private final int artId; // 如果有引用类型字段,比如自定义的Detail类,也要保证它是不可变的 private final Detail artDetail; // 构造器:对引用类型参数做防御性拷贝 public AbstractArtistry(String artName, int artId, Detail artDetail) { this.artName = Objects.requireNonNull(artName); this.artId = artId; this.artDetail = new Detail(artDetail); // 假设Detail有拷贝构造器 } // 只提供只读的getter方法,不提供setter public String getArtName() { return artName; } public int getArtId() { return artId; } public Detail getArtDetail() { // 如果Detail不是不可变类,这里也要返回其副本 return new Detail(artDetail); } }
这样不管控制器拿到的是哪个引用,都没法修改AbstractArtistry的内部状态,从根源解决问题。
如果AbstractArtistry或AbstractCommand没法改成不可变类,那就在返回HashMap的时候,对每个键和值都做深拷贝——也就是不仅拷贝容器,还要拷贝容器里的每一个对象,以及对象内部的引用类型字段。
修改你的getHashMap方法,实现深拷贝逻辑:
import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.Map; public Map<AbstractArtistry, ArrayList<AbstractCommand>> getHashMap() { Map<AbstractArtistry, ArrayList<AbstractCommand>> deepCopyMap = new LinkedHashMap<>(); for (Map.Entry<AbstractArtistry, ArrayList<AbstractCommand>> entry : this.hashmap.entrySet()) { // 深拷贝AbstractArtistry对象(假设你有拷贝构造器) AbstractArtistry artistryCopy = new AbstractArtistry(entry.getKey()); // 深拷贝ArrayList及其内部的AbstractCommand ArrayList<AbstractCommand> commandListCopy = new ArrayList<>(); for (AbstractCommand cmd : entry.getValue()) { // 假设AbstractCommand有拷贝构造器或clone方法 commandListCopy.add(new AbstractCommand(cmd)); } deepCopyMap.put(artistryCopy, commandListCopy); } return deepCopyMap; }
注意:如果AbstractArtistry或AbstractCommand内部还有引用类型的字段,你需要递归地对这些字段也做深拷贝,否则还是会存在浅拷贝的隐患。
这个方法不能单独解决对象被修改的问题,但可以防止控制器修改HashMap的结构(比如添加、删除键值对,或者替换某个键对应的ArrayList)。你可以用Collections.unmodifiableMap()来包装拷贝后的HashMap:
import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; public Map<AbstractArtistry, ArrayList<AbstractCommand>> getHashMap() { // 先做深拷贝(或用不可变类),再包装成不可修改视图 Map<AbstractArtistry, ArrayList<AbstractCommand>> copyMap = new LinkedHashMap<>(this.hashmap); // 如果需要深拷贝,这里替换成方案二的逻辑 return Collections.unmodifiableMap(copyMap); }
⚠️ 注意:这个方法只能保护HashMap本身的结构,控制器还是能修改里面的AbstractArtistry和ArrayList的内容,所以必须配合前两种方案一起使用。
内容的提问来源于stack exchange,提问作者John Lexus

