@ Daniel: Muchas gracias por esta gran respuesta!
Acabo de utilizar su código y lo modifiqué un poco en función de mis necesidades, lo que hace que sea más fácil de usar con subclases. Tal vez alguien está interesado en que también así que aquí está mi código para la clase base:
/**
* Perform a deep clone.
*
* @return Deep Clone.
* @throws CloneNotSupportedException
*/
@SuppressWarnings("unchecked")
public VersionedEntityImpl deepClone() throws CloneNotSupportedException {
Map<VersionedEntityImpl, VersionedEntityImpl> alreadyCloned = new IdentityHashMap<VersionedEntityImpl, VersionedEntityImpl>();
return deepClone(this, alreadyCloned);
}
/**
* Perform a deep clone.
*
* @param entity
* @param alreadyCloned
* @return Deep Clone.
* @throws CloneNotSupportedException
*/
@SuppressWarnings("unchecked")
protected VersionedEntityImpl deepClone(VersionedEntityImpl entity,
Map<VersionedEntityImpl, VersionedEntityImpl> alreadyCloned) throws CloneNotSupportedException {
if (entity != null) {
VersionedEntityImpl clone = alreadyCloned.get(entity);
if (clone != null) {
return clone;
}
clone = entity.clone();
alreadyCloned.put(entity, clone);
return entity.deepCloneEntity(clone, alreadyCloned);
}
return null;
}
/**
* Method performing a deep clone of an entity (circles are eliminated automatically). Calls
* deepClone(entity,alreadyCloned) recursively for each entity valued object.
*
* @param clone
* @param alreadyCloned
* @return clone
* @throws CloneNotSupportedException
*/
protected abstract VersionedEntityImpl deepCloneEntity(VersionedEntityImpl clone,
Map<VersionedEntityImpl, VersionedEntityImpl> alreadyCloned) throws CloneNotSupportedException;
Lo que hay que poner en las subclases:
@SuppressWarnings("unchecked")
@Override
protected VersionedEntityImpl deepCloneEntity(VersionedEntityImpl clone,
Map<VersionedEntityImpl, VersionedEntityImpl> alreadyCloned) throws CloneNotSupportedException {
// fill clone's attributes from original entity. Call
// deepClone(entity,alreadyCloned)
// recursively for each entity valued object.
if (this.associatedItems != null) {
List<SomeClass> listClone = new LinkedList<SomeClass>();
for (SomeClass someClass: this.associatedItems) {
listClone.add((SomeClass) super.deepClone(someClass, alreadyCloned));
}
((SomeOtherClass) clone).setAssociatedItems(listClone);
}
((SomeOtherClass) clone).setYetAnotherItem((YetAnotherClass) super.deepClone(this.yai, alreadyCloned));
return clone;
}
no es perfecta hasta el momento pero se hace el trabajo hecho muy bien por el momento :)
Para manejar referencias circulares se puede usar un IdentityMap. Esto hace un seguimiento de cada objeto que encuentra y cuando serializa o copia datos, puede usar esto para asegurarse de que maneja los objetos repetidos correctamente. p.ej. es posible que tenga el mismo objeto muchas veces en una estructura y no desee convertirlos en objetos diferentes. –