Java 克隆抽象对象

14

我在想是否有办法做到以下的操作。我有一个抽象类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()
    }
}
4个回答

6

您可以尝试使用反射:

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

在clone()方法中,你调用了getClass()。因为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

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

[编辑]我的两个克隆比他们更快;)

[编辑]为了完整起见,另一种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接口时,你可以将对象写入磁盘并从磁盘上创建一个具有相同值的副本。


我喜欢你的想法,也许将来会对我有用,但我认为我最好还是为每个子类定义克隆。很遗憾没有更简单的方法来实现这一点。 - negoose
在面向对象的方式中,在每个子类中定义它们是最好的解决方案。我同意sanbhat的观点。我担心Serializable解决方案是最慢的,而且也是一种hack方法。 - Jan Piel

2

你无法创建抽象类的深度克隆,因为它们无法实例化。你能做的只有通过使用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()方法中将参数传递到Circle构造函数中,而不是硬编码构造函数参数值? - NikolaS

2

虽然我觉得这不是一个好主意,但你可以使用反射:

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

1
您可以使用反射解决:
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);
}

但是 - 在我看来 - 它是一种糟糕的实现方式,并且很容易出错,存在许多陷阱; CloneableObject.clone() 最好不要使用!您有很多方法可以做同样的事情(例如深克隆的序列化和浅克隆),并且可以更好地控制流程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接