Artículo 11 de Java efectivo: anular el clon de forma juiciosa

Para una clase con un campo de matriz, Josh dice que si el método de clonación simplemente devuelve super.clone (), la instancia de clase resultante tendrá los valores correctos en los campos primitivos, pero su campo de matriz se referirá a la misma matriz que la instancia de clase original. Modificar el original destruirá a los invariantes y viceversa.

Utilizó el ejemplo de la implementación personalizada de Stack, estoy usando una clase simple de Student

class Student implements Cloneable { private String name; private int age; private int[] marks = {90, 70, 80}; public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public void setMarks(int[] marks) { this.marks = marks; } public Student(String name, int age) { this.name = name; this.age = age; } @Override protected Student clone() throws CloneNotSupportedException { return (Student) super.clone(); } @Override public String toString() { return "Student - Name : " + name + " Age : " + age + " Marks : " + Arrays.toString(marks); } } 

Tenga en cuenta: no invocé clone () en el campo de mi matriz en la anulación de mi método de clonación.

Entonces hice

 public class CloningDemo { public static void main(String[] args) { Student s1 = new Student("Mohit", 30); Student s2 = null; try { s2 = s1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } System.out.println("S1 : " + s1); System.out.println("S2 : " + s2); System.out.println("Updating the clone..."); s2.setName("Rohit"); s2.setAge(29); s2.setMarks(new int[]{10, 29, 30}); System.out.println("S1 : " + s1); System.out.println("S2 : " + s2); System.out.println("Updating the array elements in Original..."); s1.setMarks(new int[]{10, 10, 10}); System.out.println("S1 : " + s1); System.out.println("S2 : " + s2); } } 

Salida:

 S1 : Student - Name : Mohit Age : 30 Marks : [90, 70, 80] S2 : Student - Name : Mohit Age : 30 Marks : [90, 70, 80] Updating the clone... S1 : Student - Name : Mohit Age : 30 Marks : [90, 70, 80] S2 : Student - Name : Rohit Age : 29 Marks : [10, 29, 30] Updating the array elements in Original... S1 : Student - Name : Mohit Age : 30 Marks : [10, 10, 10] S2 : Student - Name : Rohit Age : 29 Marks : [10, 29, 30] 

Me preguntaba si cambiar la matriz en la instancia original también cambiaría la matriz en mi clon, porque mencioné anteriormente “el campo de la matriz se referirá a la misma matriz que la instancia original”

Con mi implementación de clon también debería haber visto cambios en el clon s2. La implementación adecuada hubiera sido:

 @Override protected Student clone() throws CloneNotSupportedException { Student student = (Student) super.clone(); student.marks = marks.clone(); // I am not doing this in my code. return student; } 

¿He entendido mal esto? ¿Alguien puede explicar qué está pasando?

Gracias
~ Mohit

Al llamar a s1.setMarks(new int[]{10, 10, 10}); está creando una matriz completamente nueva y escribe su referencia en las marks variables de s1 . Así que s1 y s2 refieren a dos matrices diferentes.

Si tuvieras este método:

 public void setMark(int mark, int pos) { marks[pos] = mark; } 

en la clase del Student y realizar el siguiente código:

 System.out.println("Updating the array element in Original..."); s1.setMark(999, 0); System.out.println("S1 : " + s1); System.out.println("S2 : " + s2); 

Entonces verás que esto también afecta a s2 :

 Updating the array elements in Original... S1 : Student - Name : Mohit Age : 30 Marks : [999, 70, 80] S2 : Student - Name : Rohit Age : 29 Marks : [999, 70, 80] 

(no olvide comentar la línea s2.setMarks(new int[]{10, 29, 30}); también, porque esta línea también crea una nueva referencia de matriz y elimina el enlace (matriz) entre s1 y s2 )

Este comportamiento podría describirse con un “ejemplo del mundo real”:

Imagina que tú y un amigo sostienen una cuerda, con una persona en cada extremo. Esta cuerda representa la Array que ambos se están refiriendo. Si tu amigo tira de esa cuerda (cambiando un valor en esa matriz), lo notarás. Y si tira de esa cuerda, su amigo también lo notará.

Al llamar a s1.setMarks(new int[]{...}); Tu amigo obtiene una nueva cuerda y dejará caer la primera por ella. Si él tira de esa cuerda, no lo notarás, porque ustedes dos tienen diferentes. Llamando a s2.setMarks(new int[]{...}); Obtendrás una nueva cuerda y dejarás caer la primera también. Esta es la señal para que el tercer amigo, llamado Garbage Collector , tome esa cuerda y la arroje, porque ya nadie la está usando. Pero este amigo es un poco perezoso, así que no hay garantía de que lo haga de inmediato.

Se puede usar una variable de tipo int[] para encapsular cualquiera de cuatro cosas diferentes:

  1. Los contenidos de una matriz que nunca serán modificados.

  2. El contenido de una matriz que podría modificarse, y al que no existen referencias que el propietario de la variable no conoce.

  3. La identidad de una matriz que se puede modificar y que es propiedad de otra persona.

  4. La identidad de una matriz que se puede modificar y que es propiedad del propietario de la variable, pero a la que pueden existir otras referencias.

Un método clone() no necesita clonar matrices del primer tipo, pero, aparte de un ligero costo de rendimiento, la clonación de dichas matrices es inofensiva. Un método clone() debe, sin embargo, clonar matrices del segundo tipo y abstenerse de clonar matrices del tercer tipo. Los objetos que poseen matrices del cuarto tipo generalmente no deberían implementar clone() .

No está claro si realmente desea que su código considere que la matriz es del primer tipo o del tercero; en ningún caso es necesario que su método de clonación clone la matriz. El segundo patrón es el más común cuando se utilizan variables de tipo matriz, pero su caso de uso particular no se ajusta.

Para cada variable de tipo de matriz, identifique cuál de los cuatro casos se aplica y quedará claro cómo debe proceder con la clone . Tenga en cuenta que no puede clasificar una matriz en uno de los cuatro tipos, su código probablemente está roto y debería corregirlo antes de preocuparse por la clone .

Intereting Posts