开发者

Is it possible in Java to implement something similar to Object.clone()?

The Object.clone() method in Java is pretty special, as instead of returning a copy 开发者_Go百科of the object that is to be cloned with the Object type, it returns the correct Object type. This can be better described with the following code:

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

So, could anyone explain if possible if is there anyway to replicate what happens inside Object.clone()'s method?


It is definitely true that Object.clone() does a few things that simply can not be achieved in Java.

From Josh Bloch on Design: Copy Constructor versus Cloning (emphasis mine):

Object's clone method is very tricky. It's based on field copies, and it's "extra-linguistic." It creates an object without calling a constructor. There are no guarantees that it preserves the invariants established by the constructors.

Object.clone() does something that isn't supposed to be allowed by the language. That is why, among many other reasons, clone() is broken.

(If you haven't already, you should also read his book Effective Java, to understand why he (and many others) think that Java's clone() and Cloneable is broken).


If you just want to create an object of the same class as another arbitrary object, then this is actually quite achievable, with some caveat (namely that not all types are publicly instantiable) by using reflection.

Here's an example of how to use reflection to:

  • Find out the class of an object at run-time
  • List its declared fields, methods, and constructors
  • Find its copy constructor (if any), and tries to invoke it using the given object as parameter.

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

Output:

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!

Note that this is just an example to show type can be inspected at run time and a copy constructor can be looked for and invoked. As is, it doesn't work if o is an ArrayList, because it has no constructor that takes an ArrayList (it does have one that takes a Collection, which an ArrayList is).

I'll leave it to you as an exercise on how to expand the search for the copy constructor to include these compatible overloads.


I've never heard or seen a language construct that provides you the functionality that clone give you for free.

You can imitate it, but I don't believe you can replicate the behavior.


The Objenesis library can be used to create instances of arbitrary classes even if they do not have a no-args constructor. It uses various tricks for each JVM to accomplish this. You can use that together with a bit of reflection code to copy all field values from the source object to the destination.


I think you have not even tested the code you typed here!

If you try to compile this code you get errors. First of all the return super.clone() gives you an error of "Type mismatch: cannot convert from Object to A"

Second (i will assume this was a mistype) you did not create an instance of B. You said

B = new B();

Even if we change that to

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

You will get an error because b.clone() will return an instance of class A.

So sorry but you describe does not happen... You need a cast to get what you want.

So to sum up:

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

EDIT: I'm afraid you got it wrong again. What you did returns an instance of B because you cast it to a B instance. It would return B even if you cast something completely different... For instance

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

This will print the class of B. Actually it WOULD print the class of B if it ever got there... You will get an exception before it gets there... To have what you want you have to manually override the clone method and provide your own implementation. You question is not valid at all.. You should delete it but you can't since you have upvoted answers... I would vote to close at least...


How about this?

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 {

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜