Java在替换(不更改)对象时自动更新所有引用?
所以我有一个我自己类对象的ArrayList:Java在替换(不更改)对象时自动更新所有引用?,java,reference,undo,deep-copy,redo,Java,Reference,Undo,Deep Copy,Redo,所以我有一个我自己类对象的ArrayList: public class A {...} 现在假设我有一个变量,它保存列表中的一个对象。现在我知道,如果我更改该对象(例如,在该对象上设置一个字段),这会更改保存该对象本身的内存,因此引用变量和保存列表中对象的内部数组变量仍然相同,并且都显示为相同的、现在已修改的对象。正常行为 但我也希望在替换变量所指向的列表中的对象时也是如此。那是我做的时候 myList.set(theObjectPosition, newInstanceOfA); …在这
public class A
{...}
现在假设我有一个变量,它保存列表中的一个对象。现在我知道,如果我更改该对象(例如,在该对象上设置一个字段),这会更改保存该对象本身的内存,因此引用变量和保存列表中对象的内部数组变量仍然相同,并且都显示为相同的、现在已修改的对象。正常行为
但我也希望在替换变量所指向的列表中的对象时也是如此。那是我做的时候
myList.set(theObjectPosition, newInstanceOfA);
…在这种情况下,我还希望我的变量自动引用这个新实例。实现这种行为最简单的方法是什么
这就是为什么我需要这个:
简言之,我有一组类,用于处理对ArrayList所做更改的撤消/重做操作,但我没有扩展这些更改,这无论如何也解决不了问题?。这些类被称为
UndoAction, Add, Remove, Modify (the latter 3 inherit from UndoAction)
所有类都有undo和redo方法
如果需要,描述代码的示例位于末尾:
假设我想向列表中添加一个项目:我要新建AddmyObject,然后我要修改列表中的项目,这样我就可以新建ModifymyObject,之后它会创建一个对象状态的深度副本作为备份,然后我在调用new modify后更改对象本身。。。然后我对修改对象执行撤消操作,使其执行list.setsomePos,previousDeepCopy因此这篇文章,制作的deepcopy本质上是一个新实例,它会把事情搞砸
因此,您可以想象这个list.set会带来一些问题。对替换对象的任何引用都将消失。因此,如果列表总是替换对这样的对象的引用,我就无法有效地处理这样的列表,因此我的撤消管理器注定会以这种方式失败
那么我该如何克服这些设计缺陷呢?欢迎提出任何建议
一些代码:
protected abstract class UndoAction {
protected HashSet<Integer> ids = new HashSet<Integer>();
protected Marker marker;
public UndoAction(List<E> l) {
for (E e : l) {
if (!entities.contains(e)) {
entities.add(e);
trace.append(entities.indexOf(e), new Stack<UndoAction>());
}
ids.add(entities.indexOf(e));
}
}
public boolean sameAffectedTargets(UndoAction undoAction) {
if (this.ids.containsAll(undoAction.ids) && undoAction.ids.containsAll(this.ids))
return true;
return false;
}
public Marker getMarker() {
return new Marker(this);
}
public void firstRun() {
}
public void undo() {
}
public void redo() {
}
protected List<Data> getCopyEntities() {
List<Data> l = new ArrayList<Data>();
for (Integer id : ids) {
E e = entities.get(id);
int pos = adapterList.indexOf(e);
l.add(new Data(id, pos, e));
}
return l;
}
protected List<Data> getDeepCopyEntities() {
List<Data> l = new ArrayList<Data>();
for (Integer id : ids) {
E e = DeepCopy.copy(entities.get(id));
int pos = adapterList.indexOf(entities.get(id));
l.add(new Data(id, pos, e));
}
return l;
}
public void addEntities(List<Data> datas) {
for (Data d : datas)
d.addEntity();
}
public void setEntities(List<Data> datas) {
for (Data d : datas)
d.setEntity();
}
public void removeEntities(List<Data> datas) {
for (Data d : datas)
d.removeEntity();
}
protected class Data {
public int id;
public int pos;
public E entity;
public void addEntity() {
entities.set(this.id, this.entity);
adapterList.add(this.pos, this.entity);
}
public void setEntity() {
entities.set(this.id, this.entity);
E oldEntity = adapterList.get(this.pos);
adapterList.set(this.pos, this.entity);
notifyEntityReplaced(oldEntity, adapterList.get(this.pos), this.pos);
}
public void removeEntity() {
entities.set(this.id, null);
adapterList.remove(this.entity);
}
public Data(int id, int pos, E entity) {
this.id = id;
this.pos = pos;
this.entity = entity;
}
}
}
protected class Add extends UndoAction {
protected List<Data> addBackup;
public Add(List<E> l) {
super(l);
}
@Override
public void undo() {
super.undo();
addBackup = getCopyEntities();
removeEntities(addBackup);
}
@Override
public void firstRun() {
super.firstRun();
adapterList.addAll(entities);
}
@Override
public void redo() {
super.redo();
addEntities(addBackup);
}
}
// call before modifying
protected class Modify extends UndoAction {
protected List<Data> beforeDeepCopies;
protected List<Data> afterDeepCopies;
public Modify(List<E> l) {
super(l);
}
@Override
public void undo() {
super.undo();
if (!skipModifying) {
if (afterDeepCopies == null)
afterDeepCopies = getDeepCopyEntities();
setEntities(beforeDeepCopies);
}
}
@Override
public void firstRun() {
super.firstRun();
if (!skipModifying) // TODO
beforeDeepCopies = getDeepCopyEntities();
}
@Override
public void redo() {
super.redo();
if (!skipModifying)
setEntities(afterDeepCopies);
}
}
protected class Remove extends UndoAction {
protected List<Data> removeBackup;
public Remove(List<E> l) {
super(l);
}
public List<E> getRemoved() {
List<E> l = new ArrayList<E>();
for (Data data : removeBackup)
l.add(data.entity);
return l;
}
@Override
public void undo() {
super.undo();
addEntities(removeBackup);
}
@Override
public void firstRun() {
super.firstRun();
removeBackup = getCopyEntities();
}
@Override
public void redo() {
super.redo();
removeEntities(removeBackup);
}
}
最简单的方法就是确保您的类是可变的,也就是说,您可以使用一个新对象更改内部状态
myOldObject.replaceValues(myNewObject);
如果由于某种原因,对象本身无法实现可变,只需围绕它构建一个包装器。最简单的方法就是确保您的类是可变的,也就是说,您可以使用新对象之一更改内部状态
myOldObject.replaceValues(myNewObject);
如果由于某种原因,对象本身无法实现可变,只需在其周围构建一个包装器。如果JavaClassName中的对象引用类似于C指针ClassName*,那么您似乎需要类似于指向指针ClassName**的C指针的行为 一个明显的方法是创建Holder类,其唯一目的是保存对您的类的引用。通过这种方式,您可以更改对对象本身的引用,只要所有对象都持有对持有者的引用,它们现在也将指向新替换的对象
public class AHolder{
public A aRef; // or private with getter setter.
}
如果JavaClassName中的对象引用类似于C指针ClassName*,那么您似乎需要类似于指向指针ClassName**的C指针的行为 一个明显的方法是创建Holder类,其唯一目的是保存对您的类的引用。通过这种方式,您可以更改对对象本身的引用,只要所有对象都持有对持有者的引用,它们现在也将指向新替换的对象
public class AHolder{
public A aRef; // or private with getter setter.
}
我认为你需要一个额外的间接层。与让其他代码引用A不同,您可能希望它引用堆栈,并在更改数组中的值时使用peek引用当前值,您可能希望推送新值
但是,为了保持代码的整洁,您可以通过将a转换为接口并创建一个实现(我们称之为DelegatingStackAImpl)使其更整洁,该实现将提供与a相同的接口,但支持推送和弹出,并将调用转发到当前位于堆栈顶部的实例。因此,在仍然支持这一额外的间接层的同时,您不会破坏A的现有用法。我认为您需要额外的间接层。与让其他代码引用A不同,您可能希望它引用堆栈,并在更改数组中的值时使用peek引用当前值,您可能希望推送新值
但是,为了保持代码的整洁,您可以通过将a转换为接口并创建一个实现(我们称之为DelegatingStackAImpl)使其更整洁,该实现将提供与a相同的接口,但支持推送和弹出,并将调用转发到当前位于堆栈顶部的实例。因此,在仍然支持这个额外的间接层的情况下,您不会破坏A的现有用法。如果您要替换的对象仅通过其接口引用,那么您可以首先使用A,它将委托给实际的实现。要更改实际实现,您需要修改代理
es调用处理程序。如果要替换的对象仅通过其接口引用,则首先可以使用,它将委托给实际实现。要更改实际的实现,您需要修改代理调用处理程序。我是否正确理解问题在于您还希望能够重做和操作您取消的操作?我不确定我是否理解正确-如果您执行撤消操作,您的修改操作是否应该将旧对象放回?我是否正确理解问题在于您还希望能够重做并撤消操作?我不确定我是否理解正确-如果执行撤消操作,您的修改操作是否应该将旧对象放回原处?