Java cloning abstract objects

|▌冷眼眸甩不掉的悲伤 提交于 2019-11-29 03:47:55

You can try to use reflection:

public abstract class AClonable implements Cloneable{

private String val;

public AClonable(){

}

public AClonable(String s){
    val=s;
}

public String toString(){
    return val;
}

@Override
public AClonable clone(){
    try {
        System.out.println(getClass().getCanonicalName());
        AClonable b= getClass().getDeclaredConstructor(String.class).newInstance(val);

        return b;
    } catch (InstantiationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (SecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
}

}

in the clone() method you call getClass(). Because the ACloneble ist abstract, there call will allways go to the concrete class.

   public class ClonebaleOne extends AClonable{

public ClonebaleOne(){
    super();
}

public ClonebaleOne(String s) {
    super(s);
    // TODO Auto-generated constructor stub
}

}

and

  public class ClonebaleTwo extends AClonable{

public ClonebaleTwo(){
    super();
}

public ClonebaleTwo(String s) {
    super(s);
    // TODO Auto-generated constructor stub
}

}

and finally

   public static void main(String[] args){
    AClonable one = new ClonebaleOne("One");
    AClonable tow= new ClonebaleTwo("Two");
    AClonable clone = one.clone();
    System.out.println(clone.toString());
    clone = tow.clone();
    System.out.println(clone.toString());

}

Output:

  ClonebaleOne
  One
  ClonebaleTwo
  Two

But it's more a hack than a solution

[EDIT] my two clones were faster than ;)

[EDIT] To be complete. Another implentation of clone() can be

 @Override
public AClonable clone(){
    try {
        ByteArrayOutputStream outByte = new ByteArrayOutputStream();
        ObjectOutputStream outObj = new ObjectOutputStream(outByte);
        ByteArrayInputStream inByte;
        ObjectInputStream inObject;
        outObj.writeObject(this);
        outObj.close();
        byte[] buffer = outByte.toByteArray();
        inByte = new ByteArrayInputStream(buffer);
        inObject = new ObjectInputStream(inByte);
        @SuppressWarnings("unchecked")
        Object deepcopy =  inObject.readObject();
        inObject.close();
        return (AClonable) deepcopy;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

when your abstract class implements Serialazable. There you write your object to disc and create a copy with the value from the disc.

You can't create deep clone of abstract class because they can't be instantiated. All you can do is shallow cloning by using Object.clone() or returning this

@Override
public Object clone() throws CloneNotSupportedException {
    return super.clone();
}

or

@Override
public Object clone() throws CloneNotSupportedException {
    return this;
}

An abstract class can act as a reference, and it cannot have an instance so shallow cloning works in this case

OR

As a better approach, you can declare clone() as abstract and ask child class to define it, something like this

abstract class Shape {

    private String str;

    public Shape(String str) {
        this.str = str;
    }

    public abstract Shape clone();

    public String toString() {
        return str;
    }
}

class Circle extends Shape {

    public Circle(String str) {
        super(str);
    }

    @Override
    public Shape clone() {
        return new Circle("circle");
    }

}

Although I doubt it is a good idea, you could use reflection:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test {

    public static void main(String[] args) {        
        Square s1 = new Square("test");
        Square s2 = (Square) s1.clone();

        // show that s2 contains the same data  
        System.out.println(s2);
        // show that s1 and s2 are really different objects
        System.out.println(s1 == s2);
    }

    public static abstract class Shape {
        private String str;

        public Shape(String str) {
            this.str = str;
        }

        public Shape clone() {          
            try {
                Class<?> cl = this.getClass();
                Constructor<?> cons = cl.getConstructor(String.class);
                return (Shape) cons.newInstance(this.toString());           
            } catch (NoSuchMethodException | SecurityException |
                     InstantiationException | IllegalAccessException |
                     IllegalArgumentException | InvocationTargetException e) {  
                e.printStackTrace();
            }           

            return null;
        }

        @Override
        public String toString() {
            return str;
        }
    }

    public static class Square extends Shape {
        public Square(String str) {
            super(str);
        }
    }   
}

You can resolve with reflection:

public abstract class Shape {

    private String str;

    public Shape()  {

    }

    protected Shape(String str) {
        this.str = str;
    }

    public Shape clone() throws CloneNotSupportedException
    {
        try {
            return (Shape)getClass().getDeclaredConstructor(String.class).newInstance(this.toString());
        } catch (Exception e) {
            throw new CloneNotSupportedException();
        }
    }

    public String toString() {
        return "shape";
    }

public class Round extends Shape
{
    public Round()
    {
        super();
    }
    protected Round(String str) {
        super(str);
    }

    @Override
    public String toString() {
        return "round";
    }
}

main(){
  Shape round = new Round();        
  Shape clone = round.clone();
  System.out.println(round);
  System.out.println(clone);
}

but - IMO - is a poor implementation and error-prone with a lot of pits; the best use of Cloneable and Object.clone() is to not use them! You have a lot of way to do the same thing (like serialization for deep-clone) and shallow-clone that allow your a better control of flow.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!