在Java中是否可以实现类似于Object.clone()的东西?

时间:2010-03-25 17:56:15

标签: java

Java中的Object.clone()方法非常特殊,因为它不返回要使用Object类型克隆的对象的副本,而是返回正确的Object类型。使用以下代码可以更好地描述这一点:

class A implements Cloneable
{
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class B extends A {
}

public class MainABC {
    public static void main(String[] args) throws CloneNotSupportedException {
        B b = new B();
        B b1 = (B)b.clone(); //see here that we are using A's .clone(). The only
                             //thing it does is call Object's clone().
        System.out.println(b1.getClass()); //but as we see here, its not an Object
                                           //its a B!
    }
}

那么,任何人都可以解释一下,如果有可能无论如何都要复制Object.clone()方法中发生的事情吗?

5 个答案:

答案 0 :(得分:2)

Object.clone()确实做了一些在Java中无法实现的事情。

来自Josh Bloch on Design: Copy Constructor versus Cloning(强调我的):

  

对象的克隆方法非常棘手。它基于字段副本,而且“语言外”。 它创建一个对象而不调用构造函数。无法保证它保留构造函数建立的不变量

Object.clone()执行语言不应该允许的内容。这就是为什么,除了许多其他原因之外,clone()被打破了。

(如果你还没有,你也应该阅读他的书 Effective Java ,以了解为什么他(以及其他许多人)认为Java的clone()Cloneable坏了)。


如果你只是想创建一个与另一个任意对象相同的类的对象,那么这实际上是可以实现的,但有一些警告(即并非所有类型都是可公开实例化的)通过使用反射。

以下是如何使用反射的示例:

  • 在运行时找出对象的类
  • 列出其声明的字段,方法和构造函数
  • 找到它的复制构造函数(如果有的话),并尝试使用给定的对象作为调用它 参数。

import java.lang.reflect.*;

public class NewInstance {
   static void print(String label, Object[] arr) {
      System.out.println(label);
      for (Object o : arr) {
         System.out.println(o);
      }
      System.out.println("---");
   }

   static Object newInstance(Object o) {
      Class<?> c = o.getClass();
      System.out.println("Class is " + c);
      print("FIELDS:", c.getDeclaredFields());
      print("METHODS:", c.getDeclaredMethods());
      print("CONSTRUCTORS:", c.getDeclaredConstructors());

      try {
         Constructor<?> cc = c.getDeclaredConstructor(c);
         o = cc.newInstance(o);
      } catch (NoSuchMethodException e) {
         System.out.println("No copy constructor found!");
      } catch (IllegalAccessException e) {
         System.out.println("Copy constructor inaccessible!");
      } catch (InstantiationException e) {
         System.out.println("Instantiation failed!");
      } catch (InvocationTargetException e) {
         System.out.println("Copy constructor threw " + e.getCause());
      }
      return o;
   }

   public static void main(String args[]) {
      Object o1 = "hello";
      Object o2 = newInstance(o1);
      boolean success = (o1 != o2) && (o1.equals(o2));
      System.out.println("Attempt " + (success ? "succeeded!" : "failed :("));
   }
}

输出:

Class is class java.lang.String
FIELDS:
// (omitted)
METHODS:
// (omitted)
CONSTRUCTORS:
public java.lang.String()
public java.lang.String(java.lang.String) // this is what we're looking for!
// (rest omitted)
---
Attempt succeeded!

请注意,这只是一个示例,可以在运行时检查显示类型,并且可以查找和调用复制构造函数。如果oArrayList,它就不起作用,因为它没有带ArrayList的构造函数(它有一个带Collection的构造函数,一个ArrayList是。)

我将把它留给你作为练习如何扩展搜索复制构造函数以包含这些兼容的重载。

答案 1 :(得分:1)

我从未听过或看过一种语言结构,为您提供clone免费提供的功能。

你可以模仿它,但我不相信你可以复制这种行为。

答案 2 :(得分:1)

Objenesis library可用于创建任意类的实例,即使它们没有no-args构造函数也是如此。它为每个JVM使用各种技巧来实现这一目标。您可以将它与一些反射代码一起使用,将所有字段值从源对象复制到目标。

答案 3 :(得分:0)

我认为你甚至没有测试过你在这里输入的代码!

如果您尝试编译此代码,则会出现错误。首先返回super.clone()会给出错误“类型不匹配:无法从Object转换为A”

第二个(我假设这是一个错误的类型)你没有创建B的实例。你说

B = new B();

即使我们将其改为

B b = new B();
B b1 = b.clone();

您将收到错误,因为b.clone()将返回A类的实例。

很抱歉,但你的描述并没有发生......你需要一个演员才能得到你想要的东西。

总结一下:

public class A extends Object {
    public A clone() {
        return super.clone(); // Error "Type mismatch: cannot convert from Object to A"
    }

    public static void main(String[] args) {
        B b = new B();
        B b1 = b.clone(); // Error "Type mismatch: cannot convert from A to B"
    }
}

class B extends A {
}

编辑:我怕你再次弄错了。你所做的是返回B的一个实例,因为你将它转换为B实例。即使你施展完全不同的东西,它也会返回B ......例如

B b = (B)(new JLabel());
System.out.println(b.class);

这将打印出B类。实际上如果它到达那里它将会打印出B类...你会在它到达之前得到一个例外...要拥有你想要的东西你必须手动覆盖clone方法并提供您自己的实现。你的问题根本无效..你应该删除它,但你不能,因为你有赞成答案......我会投票至少关闭......

答案 4 :(得分:0)

这个怎么样?

public class A {
    public static void main(String[] args) {
        B b = new B();
        B b1 = (B)b.getNew();
        System.out.println(b1.getClass());
    }

    public Object getNew() {
        try {
            return getClass().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}

class B extends A {

}