Java cloning abstract objects

后端 未结 4 1028
执笔经年
执笔经年 2020-12-16 15:37

I\'m wondering if there is any way to do the following. I have an abstract class, Shape, and all its different subclasses and I want to override the clone metho

相关标签:
4条回答
  • 2020-12-16 15:46

    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.

    0 讨论(0)
  • 2020-12-16 15:46

    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.

    0 讨论(0)
  • 2020-12-16 15:49

    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);
            }
        }   
    }
    
    0 讨论(0)
  • 2020-12-16 16:11

    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");
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题