usar - Cómo clonar un objeto Java con el método clone()
metodo clone arraylist java (8)
No entiendo el mecanismo de clonación de objeto personalizado. Por ejemplo:
public class Main{
public static void main(String [] args) {
Person person = new Person();
person.setFname("Bill");
person.setLname("Hook");
Person cloned = (Person)person.clone();
System.out.println(cloned.getFname() + " " + cloned.getLname());
}
}
class Person implements Cloneable{
private String fname;
private String lname;
public Object clone() {
Person person = new Person();
person.setFname(this.fname);
person.setLname(this.lname);
return person;
}
public void setFname(String fname) {
this.fname = fname;
}
public void setLname(String lname){
this.lname = lname;
}
public String getFname(){
return fname;
}
public String getLname() {
return lname;
}
}
Este es un ejemplo que muestra la forma correcta de clonar como escriben los libros. Pero puedo eliminar implementos Clonables en la definición del nombre de clase y recibo el mismo resultado.
¿Entonces no entiendo la propuesta de Cloneable y por qué el método clone () se define en la clase Object?
El clone()
en la clase Object
hace una copia superficial de la memoria en lugar de llamar a métodos como el constructor. Para llamar a clone()
en cualquier objeto que no implemente clone()
, necesita implementar la interfaz Clonable
.
Si reemplaza el método clone()
no tiene que implementar esa interfaz.
Al igual que dice JavaDoc, esto daría como resultado una excepción:
class A {
private StringBuilder sb; //just some arbitrary member
}
...
new A().clone(); //this will result in an exception, since A does neither implement Clonable nor override clone()
Si A
en el ejemplo implementaría Clonable
calling clone()
(la versión del Object
) resultaría en una nueva instancia A
que hace referencia al mismo StringBuilder, es decir, los cambios a sb
en la instancia clonada darían como resultado cambios a la sb
en el A
original ejemplo.
Eso significa una copia superficial y esa es la razón por la que generalmente es mejor anular clone()
.
Edición: al igual que una nota al margen, el uso de covarianza de tipo de retorno haría que su clone()
anulado sea más explícito:
public Person clone() {
...
}
El clon no es maduro como dice Joshua Bloch:
El método de clonación está destinado a hacer una copia profunda. Asegúrese de entender la diferencia entre las copias profundas y superficiales. En su caso, un constructor de copias puede ser el patrón que desee. Sin embargo, en algunos casos no puede utilizar este patrón, por ejemplo, porque está subclasificando la clase X y no tiene acceso al constructor de X que necesita. Si X anula su método de clonación correctamente (si es necesario), podría hacer una copia de la siguiente manera:
class Y extends X implements Cloneable {
private SomeType field; // a field that needs copying in order to get a deep copy of a Y object
...
@Override
public Y clone() {
final Y clone;
try {
clone = (Y) super.clone();
}
catch (CloneNotSupportedException ex) {
throw new RuntimeException("superclass messed up", ex);
}
clone.field = this.field.clone();
return clone;
}
}
En general al anular su método de clonación:
- Hacer el tipo de devolución más específico.
- Empieza por llamar a
super.clone()
- No incluya la cláusula de lanzamientos cuando sepa que
clone()
también funcionará para cualquier subclase (debilidad del patrón de clonación; haga la final de la clase si es posible) - Deje solo los campos inmutables y primitivos, pero clone los campos de objetos mutables manualmente después de la llamada a
super.clone()
(otra debilidad del patrón de clonación, ya que estos campos no pueden ser definitivos)
El método clone()
de Object
(que finalmente se llamará cuando todas las superclases obedezcan el contrato) hace una copia superficial y se ocupa del tipo de tiempo de ejecución correcto del nuevo objeto. Observe cómo no se llama a ningún constructor en todo el proceso.
Si desea poder llamar a clone()
en instancias, entonces implemente la interfaz Cloneable
y haga público el método. Si no desea poder llamarlo en instancias, pero quiere asegurarse de que las subclases puedan llamar a su super.clone()
y obtener lo que necesitan, entonces no implemente Cloneable
y mantenga el método protected
si su La superclase no la ha declarado ya pública.
El patrón de clonación es difícil y tiene muchos escollos. Asegúrate de que sea lo que necesitas. Considere copiar constructores, o un método de fábrica estático.
En su ejemplo, no está haciendo una clonación real. Está anulado el método clone () de la clase de objeto y se le asigna su propia implementación. pero en su método de clonación está creando un nuevo objeto Person. y devolviéndolo. Entonces, en este caso, el objeto real no se clona.
Entonces tu método de clonación debería ser como:
public Object clone() {
return super.clone();
}
Así que aquí el clon será manejado por el método de superclase.
Es el mismo propósito que cualquier interfaz de este tipo. Principalmente, permite que los métodos (etc.) acepten CUALQUIER objeto Clonable y tengan acceso al método que necesitan sin limitarse a un objeto específico. Es cierto que Clonable es probablemente una de las interfaces menos útiles a este respecto, pero ciertamente hay lugares donde puede que lo desee. Si desea más de una idea, considere la interfaz Comparable que, por ejemplo, le permite tener listas ordenadas (porque la lista no necesita saber qué es el objeto, solo que se pueden comparar).
La JVM puede clonar el objeto para usted y, por lo tanto, no debe construir una nueva persona por sí mismo. Solo usa este código:
class Person implements Cloneable {
// ...
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
o
class Person implements Cloneable {
// ...
@Override
public Object clone() {
try {
return super.clone();
}
catch (CloneNotSupportedException e) {
throw new Error("Something impossible just happened");
}
}
}
Esto funcionará incluso si la clase Persona está subclasificada, mientras que su implementación de clonación siempre creará una instancia de Persona (y no una instancia de Empleado para un Empleado, por ejemplo).
No hay necesidad de crear el objeto explícitamente aquí en el método clone()
. Simplemente llamando a super.clone()
creará la copia de este objeto. Se llevará a cabo el clon superficial.
Si no declara una interfaz clonable, debería obtener la excepción CloneNotSupportException cuando llame al método de clonación. Si declara y luego llama al método de clonación, se realizará una copia superficial.