Problem cloning object in java

3

I'm certainly confused when trying to clone an object.

On the one hand and tried to use the interface Cloneable

@Override
protected Object clone() throws CloneNotSupportedException {
    // TODO Auto-generated method stub
    Consultas consultas=new Consultas();

    consultas.id = id;
    consultas.idOrg = idOrg;
    consultas.idUsu = idUsu;
    consultas.consulta = consulta;
    consultas.estado = estado;
    consultas.fecha = fecha;
    consultas.leidoAnt = leidoAnt;
    consultas.idPerfil = idPerfil;
    consultas.fechaUltima = fechaUltima;
    consultas.idTema = idTema;
    consultas.ficheroAdjunto = ficheroAdjunto;
    consultas.leidoConc = leidoConc;
    consultas.marca = marca;
    consultas.solicitadoTeamviewer=solicitadoTeamviewer;

    return consultas;
}

public Object clonar(){
    try {
        return this.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
        return null;
    }
}

Well, this does not help me, because although I copy the object well, when I try to modify some field, I modify it in both objects, copying and copying, so I understand that with clone() is also copied the reference in memory.

Well, seeing that it does not work, I have ventured to create a small method to clone objects.

public static Object clonar(Object acopiar)  {
        // TODO Auto-generated method stub
        Class<?> clazz;
        Object obj=null;
        try {
            clazz = Class.forName(acopiar.getClass().getName());
            obj=clazz.newInstance();
            Map<String,Method> mapa=new HashMap<String,Method>();
            Method[] metodos=clazz.getDeclaredMethods();
            for(Method method2:metodos){
                mapa.put(method2.getName(), method2);
            }

            for(Method method:metodos){

                if(method.getName().startsWith("get")){
                    try{
                        Method setter;
                        String nombre=method.getName();
                        Object value=method.invoke(acopiar);
                        setter = obj.getClass().getMethod(nombre.replace("get","set"),mapa.get(nombre.replace("get","set")).getParameterTypes());
                        setter.invoke(obj, value);
                    }catch(Exception e){
                        System.out.println("No se ha podido settear el campo: "+method.getName()+" : "+e.getClass()+" --> "+e.getMessage());
                    }
                }

            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e1) {
            System.out.println("Exception:  "+e1.getClass()+" --> "+e1.getMessage());
        }

        return obj;
    }

Well, even with this method, which creates the object well with all its properties, when I modify the source object, the copy is modified too!

Consultas aux=itConsultas.next();
                        Consultas copia=(Consultas)Utilidades.clonar(aux);
                        aux.getId().setIdConsulta(Integer.parseInt(idsSplited[0]));
                        aux.getId().setIdRespuesta(++lastValue);
                        copias.add(copia);

This is where I try to copy an object.

If someone told me that I could be doing wrong, or that I was missing, I would appreciate it.

EDIT: I have tried to create a copy-constructor which does not work for me, I still share an instance, I have also overwritten the method clone  as @Fernando told me and they continue to share an instance (although, as Luiggi said, this did not work). Any more suggestions? The truth is that I do not know what else can be ...

Here the code of the constructor-copy to see if something happened to me

public Consultas(Consultas a_copiar) {

        this.id = a_copiar.id;
        this.idOrg = a_copiar.idOrg;
        this.idUsu = a_copiar.idUsu;
        this.consulta = a_copiar.consulta;
        this.estado = a_copiar.estado;
        this.fecha = a_copiar.fecha;
        this.leidoAnt = a_copiar.leidoAnt;
        this.idPerfil = a_copiar.idPerfil;
        this.fechaUltima = a_copiar.fechaUltima;
        this.idTema = a_copiar.idTema;
        this.ficheroAdjunto = a_copiar.ficheroAdjunto;
        this.leidoConc = a_copiar.leidoConc;
        this.marca = a_copiar.marca;
        this.solicitadoTeamviewer=a_copiar.solicitadoTeamviewer;


        }
    
asked by Hictus 17.04.2017 в 10:59
source

2 answers

1

your first approach was correct (implement the Clonable interface) but the implementation was not. You can not do return super.clone(); because it has the standard behavior that you do not want. To get it you have to create an object and assign it the properties. I leave an example for you to see how it would be done (although there are variants, like the one you have used with reflection):

Class to clone:

public class TestClonarMyClass implements Cloneable {

    private String prop1;
    private String prop2;

    public String getProp1() {
        return prop1;
    }
    public void setProp1(String prop1) {
        this.prop1 = prop1;
    }
    public String getProp2() {
        return prop2;
    }
    public void setProp2(String prop2) {
        this.prop2 = prop2;
    }

    @Override
    public String toString() {
        return "MyClass [prop1=" + prop1 + ", prop2=" + prop2 + "]";
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        final TestClonarMyClass newObj = new TestClonarMyClass();

        newObj.setProp1(prop1);
        newObj.setProp2(prop2);

        return newObj;
    }

}

Sample code:

public class TestClonar {   

    public static void main(String[] args) throws CloneNotSupportedException {
        final TestClonarMyClass obj1 = new TestClonarMyClass();
        obj1.setProp1("prop1");
        obj1.setProp2("prop2");

        final TestClonarMyClass obj2 = (TestClonarMyClass) obj1.clone();
        obj2.setProp1("prop1-b");
        obj2.setProp2("prop2-b");

        System.out.println("OBJ1" + obj1);
        System.out.println("\nOBJ2" + obj2);
    }
}

You will see that the output of the example is this:

OBJ1MyClass [prop1=prop1, prop2=prop2]

OBJ2MyClass [prop1=prop1-b, prop2=prop2-b]

As you will see, the values of the properties are different.

    
answered by 17.04.2017 в 12:41
1

The super.clone method based on Object#clone returns what is known as a shallow copy. The code that you have implemented using reflection also returns a surface copy where you only copy the state of one object to another, and that only copies the state of the getters and setters declared in that class, you are not evaluating those of the parent methods and some field that does not have these methods (I could say that the method you have implemented has use for simple classes). That is why when you copy an object, when copying the primitive data there is no problem, but when copying a reference (that is, an object) copies the reference as such and when modifying the state in an object it is "reflected" in the another.

What you are looking for is called deep copy. For this, there are several options:

  • If you have few classes, I recommend creating a copy-constructor. That is, they receive an instance of themselves and allow to "copy" all the properties.
  • If you have too many classes or have very complex classes (many attributes, including references or collections that store references), another strategy is that your classes to be cloned and all the classes declared inside implement the Serializable interface, so that you only serialize and deserialize the object. The deserialized object is already a totally new copy. Beware that this method has a price and is the impact in memory: if your object to be cloned is very heavy in memory, when you serialize it you may take up too much space and in your application skip an OOM.
  • Use an in-depth copy library such as Java Deep Cloning using reflection or SerializationUtils using serialization / deserialization in memory.
  • Based on the case of serialization / deserialization, instead of using Serializable and Java serialization, serialize the information to a textual format like JSON, XML, YAML or other, and use a library like Jackson for serialization / deserialization of information. The deserialized object is already a totally new copy of the information you are looking for.

More information:

answered by 17.04.2017 в 16:26