java - pasar - Copia profunda, copia superficial, clon
metodo clone java (4)
Necesito una aclaración sobre las diferencias entre la copia profunda, la copia superficial y el clon en Java
Desafortunadamente, "copia superficial", "copia profunda" y "clonar" son términos bastante mal definidos.
En el contexto de Java, primero tenemos que hacer una distinción entre "copiar un valor" y "copiar un objeto".
int a = 1;
int b = a; // copying a value
int[] s = new int[]{42};
int[] t = s; // copying a value (the object reference for the array above)
StringBuffer sb = new StringBuffer("Hi mom");
// copying an object.
StringBuffer sb2 = new StringBuffer(sb);
En resumen, una asignación de una referencia a una variable cuyo tipo es un tipo de referencia es "copiar un valor" donde el valor es la referencia del objeto. Para copiar un objeto, algo debe usar new
, ya sea explícitamente o debajo del capó.
Ahora para la copia de objetos "superficial" versus "profunda". La copia superficial generalmente significa copiar solo un nivel de un objeto, mientras que la copia profunda generalmente significa copiar más de un nivel. El problema está en decidir qué queremos decir con un nivel. Considera esto:
public class Example {
public int foo;
public int[] bar;
public Example() { };
public Example(int foo, int[] bar) { this.foo = foo; this.bar = bar; };
}
Example eg1 = new Example(1, new int[]{1, 2});
Example eg2 = ...
La interpretación normal es que una copia "superficial" de eg1
sería un nuevo objeto Example
cuyo foo
es igual a 1 y cuyo campo de bar
refiere a la misma matriz que en el original; p.ej
Example eg2 = new Example(eg1.foo, eg1.bar);
La interpretación normal de una copia "profunda" de eg1
sería un nuevo objeto Example
cuyo foo
es igual a 1 y cuyo campo de bar
refiere a una copia de la matriz original; p.ej
Example eg2 = new Example(eg1.foo, Arrays.copy(eg1.bar));
(Las personas que provienen de un fondo C / C ++ podrían decir que una asignación de referencia produce una copia superficial. Sin embargo, eso no es lo que normalmente queremos decir con copia poco profunda en el contexto de Java ...)
Existen dos preguntas / áreas de incertidumbre más:
¿Qué tan profundo es profundo? ¿Se detiene en dos niveles? Tres niveles? ¿Significa todo el gráfico de objetos conectados?
¿Qué pasa con los tipos de datos encapsulados? por ejemplo, una cadena? Una Cadena no es solo un objeto. De hecho, es un "objeto" con algunos campos escalares y una referencia a una matriz de caracteres. Sin embargo, la matriz de caracteres está completamente oculta por la API. Entonces, cuando hablamos de copiar un String, ¿tiene sentido llamarlo copia "superficial" o "profunda"? ¿O deberíamos simplemente llamarlo una copia?
Por último, clonar El clon es un método que existe en todas las clases (y matrices) que generalmente se cree que produce una copia del objeto de destino. Sin embargo:
La especificación de este método deliberadamente no dice si se trata de una copia superficial o profunda (suponiendo que sea una distinción significativa).
De hecho, la especificación ni siquiera establece específicamente que el clon produce un nuevo objeto.
Esto es lo que dice el javadoc :
"Crea y devuelve una copia de este objeto. El significado preciso de" copiar "puede depender de la clase del objeto. La intención general es que, para cualquier objeto x, la expresión
x.clone() != x
sea ​​verdadera , y que la expresiónx.clone().getClass() == x.getClass()
será verdadera, pero estos no son requisitos absolutos. Si bien es típico quex.clone().equals(x)
ser cierto, este no es un requisito absoluto ".
Tenga en cuenta que esto indica que en un extremo el clon podría ser el objeto de destino, y en el otro extremo el clon podría no ser igual al original. Y esto supone que el clon es incluso compatible.
En resumen, clonar significa potencialmente algo diferente para cada clase de Java.
Algunas personas discuten (como @supercat lo hace en los comentarios) que el método de clone()
Java clone()
está roto. Pero creo que la conclusión correcta es que el concepto de clon se rompe en el contexto de OO. AFAIK, es imposible desarrollar un modelo unificado de clonación que sea consistente y utilizable en todos los tipos de objetos.
El término "clonar" es ambiguo (aunque la biblioteca de clases Java incluye una interfaz Cloneable ) y puede hacer referencia a una copia profunda o una copia superficial. Las copias profundas / superficiales no están vinculadas específicamente con Java, pero son un concepto general relacionado con la realización de una copia de un objeto, y se refieren a cómo se copian también los miembros de un objeto.
Como ejemplo, digamos que tienes una clase de persona:
class Person {
String name;
List<String> emailAddresses
}
¿Cómo se clonan los objetos de esta clase? Si está realizando una copia superficial, puede copiar el nombre y poner una referencia a emailAddresses
de emailAddresses
en el nuevo objeto. Pero si modificó los contenidos de la lista de emailAddresses
de emailAddresses
, estaría modificando la lista en ambas copias (ya que así es como funcionan las referencias de objetos).
Una copia profunda significaría que copia de manera recursiva a cada miembro, por lo que necesitaría crear una nueva List
para la nueva Person
y luego copiar el contenido del objeto antiguo al nuevo.
Aunque el ejemplo anterior es trivial, las diferencias entre las copias profundas y superficiales son significativas y tienen un gran impacto en cualquier aplicación, especialmente si está intentando diseñar un método genérico de clonación por adelantado, sin saber cómo alguien podría usarlo más adelante. Hay momentos en los que necesita semántica profunda o superficial, o algún híbrido en el que copia profundamente algunos miembros pero no otros.
Los términos "copia superficial" y "copia profunda" son un poco vagas; Yo sugeriría usar los términos "clon de miembro" y lo que yo llamaría un "clon semántico". Un "clon de miembro" de un objeto es un objeto nuevo, del mismo tipo de tiempo de ejecución que el original, para cada campo, el sistema realiza efectivamente "newObject.field = oldObject.field". La base Object.Clone () realiza un clon de miembro; la clonación de miembros es generalmente el punto de partida correcto para clonar un objeto, pero en la mayoría de los casos se requerirá algún "trabajo de reparación" después de un clon de miembro. En muchos casos, el intento de utilizar un objeto producido a través de clonación de miembro sin realizar primero la corrección necesaria provocará que sucedan cosas malas, incluida la corrupción del objeto que fue clonado y posiblemente otros objetos también. Algunas personas usan el término "clonación superficial" para referirse a la clonación de miembros, pero ese no es el único uso del término.
Un "clon semántico" es un objeto que contiene los mismos datos que el original, desde el punto de vista del tipo . Para examinar, considere una BigList que contenga un Array> y un conteo. Un clon de nivel semántico de tal objeto realizaría un clon de miembro, luego reemplazará la Matriz> por una nueva matriz, creará nuevas matrices anidadas y copiará todas las T de las matrices originales a las nuevas. No intentaría ningún tipo de clonación profunda de los T''s . Irónicamente, algunas personas se refieren a la clonación de "clonación superficial", mientras que otros la llaman "clonación profunda". No es exactamente una terminología útil.
Si bien hay casos en los que la clonación verdaderamente profunda (copia recursiva de todos los tipos mutables) es útil, solo debe realizarse por tipos cuyos componentes están diseñados para dicha arquitectura. En muchos casos, la clonación realmente profunda es excesiva y puede interferir con situaciones en las que lo que se necesita es, de hecho, un objeto cuyos contenidos visibles se refieren a los mismos objetos que otro (es decir, una copia de nivel semántico). En los casos en que los contenidos visibles de un objeto se derivan de forma recursiva de otros objetos, un clon de nivel semántico implicaría un clon profundo recursivo, pero en los casos en que los contenidos visibles sean solo genéricos, el código no debería clonar a ciegas todo parece que podría ser capaz de clonación profunda.
- Copia profunda: clona este objeto y cada referencia a cualquier otro objeto que tenga
- Copia superficial: clona este objeto y conserva sus referencias
- Object clone () lanza CloneNotSupportedException: no se especifica si esto debería devolver una copia profunda o superficial, pero como mínimo: o.clone ()! = O