【问题标题】:Java cloning abstract objectsJava 克隆抽象对象
【发布时间】:2013-08-09 12:09:31
【问题描述】:

我想知道是否有任何方法可以执行以下操作。我有一个抽象类Shape,以及它所有不同的子类,我想重写克隆方法。我想要在该方法中做的就是从当前的toString() 创建一个新的Shape。显然我不能执行以下操作,因为Shape 是抽象的。是否有另一种方法可以做到这一点,因为在每个子类中重写克隆只是为了简单的名称更改似乎没用。

public abstract class Shape {

    public Shape(String str) {
        // Create object from string representation
    }

    public Shape clone() {
        // Need new way to do this
        return new Shape(this.toString());   
    }

    public String toString() {
        // Correctly overriden toString()
    }
}

【问题讨论】:

    标签: java overriding clone abstract-class abstract


    【解决方案1】:

    你可以尝试使用反射:

    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;
    }
    

    }

    在你调用 getClass() 的 clone() 方法中。因为 ACloneble 是抽象的,所以调用总是会转到具体的类。

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

    }

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

    }

    最后

       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());
    
    }
    

    输出:

      ClonebaleOne
      One
      ClonebaleTwo
      Two
    

    但这更像是一种破解而不是解决方案

    [编辑] 我的两个克隆比 ;)

    [编辑] 完整。 clone() 的另一种实现方式可以是

     @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;
    }
    

    当您的抽象类实现 Serialazable 时。在那里,您将对象写入光盘并使用光盘中的值创建一个副本。

    【讨论】:

    • 我喜欢你的想法,也许它将来会派上用场,但我认为我最好为每个子类定义克隆。太糟糕了,没有更简单的方法来实现这一点。
    • 在每个子类中定义它们是面向对象方式的最佳解决方案。我同意sanbhat。我担心 Serialazable 解决方案是最慢的,而且它也是一个 hack。
    【解决方案2】:

    您无法创建abstract 类的深度克隆,因为它们无法实例化。您所能做的就是浅克隆,使用Object.clone() 或返回this

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

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

    一个抽象类可以作为一个引用,它不能有一个实例,所以在这种情况下浅克隆是可行的

    作为一种更好的方法,您可以将clone() 声明为abstract 并要求子类定义它,就像这样

    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");
        }
    
    }
    

    【讨论】:

    • 如果Shape 有多个字段被传递给构造函数参数,你将如何将参数传递给循环构造函数内的Circle 类的重写clone() 方法没有硬编码构造函数参数价值观?
    【解决方案3】:

    虽然我怀疑这是个好主意,但您可以使用反射:

    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);
            }
        }   
    }
    

    【讨论】:

      【解决方案4】:

      你可以用反射解决:

      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);
      }
      

      但是 - IMO - 是一个糟糕的实现并且容易出错并且有很多坑; CloneableObject.clone() 的最佳用途是不要使用它们!你有很多方法可以做同样的事情(比如深度克隆的序列化)和浅克隆,让你更好地控制流程。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-11-07
        • 2017-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多