开发者

How to clone an Object you dont know the type of?

开发者 https://www.devze.com 2023-01-12 11:24 出处:网络
its easier to explain in code so here Object anObj; anObj = new MyObj(); anObj = new Rectangle(); anObj.clone();//this doesnt exist because its on the root Object class

its easier to explain in code so here

Object anObj;
anObj = new MyObj();
anObj = new Rectangle();
anObj.clone();//this doesnt exist because its on the root Object class

what can i use instead开发者_运维技巧 of the Object.clone() method in this example?

----------------------- extra info ------------------------------

I have added extra info but it seems to have gone in the middle of all the answers, so here it is again so it can be read.

Hi all these are all really helpful on topic of cloning or copying, which i now need to think about. but they dont help with the initial question. maybe more info from me will help you understand what im after.

I am overriding the clone for each of my objects and adding all the other clone and copy methods needed to completely clone the object, this includes adding a custom method to copy a bufferedimage. ie:-

public Object clone() {//i copied from 'thelost's answer
    try { 
        CloningExample copy = (CloningExample)super.clone(); 
        copy.names = (LinkedList)names.clone(); 
        return copy; 
    } catch (CloneNotSupportedException e) { 
        return null; 
    } 
}

but i have one variable in my class that is an Object but because it hold various other objects of different types, each of my types will have a clone method, but short of checking if its each of my types and then calling clone() on my type, which would be very long as i have many types, i cannot see how to copy or clone the object easily. is there a way os should i just write a static method like this?

static findTypeAndCopy(Object thisobj){ 
    if(thisobj==null) 
        return null;

    if(thisobj instanceOf MyObj1){ 
        return ((MyObj1)thisobj).clone(); 
    }else if(thisobj instanceOf MyObj2){ 
        return ((MyObj2)thisobj).clone(); 
    ... 
    etc
}

???


You seem to have realized that Cloneable in Java is broken.

Here are some quotes from an interview with Josh Bloch, author of Effective Java 2nd Edition:

If you've read the item about cloning in my book, especially if you read between the lines, you will know that I think clone is deeply broken. There are a few design flaws, the biggest of which is that the Cloneable interface does not have a clone method. And that means it simply doesn't work: making something Cloneable doesn't say anything about what you can do with it. Instead, it says something about what it can do internally. It says that if by calling super.clone repeatedly it ends up calling Object's clone method, this method will return a field copy of the original.

But it doesn't say anything about what you can do with an object that implements the Cloneable interface, which means that you can't do a polymorphic clone operation.

Here are some quotes from the book, Item 11: Override clone judiciously:

[...] you are better off providing alternative means of object copying, or simply not providing the capability.

[...] A fine approach to object copying is to provide copy constructor or copy factory. A copy constructor is simply a constructor that takes a single argument whose type is the class containing the constructor:

public Yum(Yum yum);

A copy factory is the static factory analog of a copy constructor:

public static Yum newInstance(Yum yum);

Related questions

  • Why is the clone() method protected in java.lang.Object?
  • Why people are so afraid of using clone() (on collection and JDK classes) ?
  • How to properly override clone method?
  • clone() vs copy constructor vs factory method??

Alternative: Cloneable 2.0

If you really insist on having a Cloneable-like functionality that isn't broken, you can write something like this (generified for extra jazz):

public class DupableExample {

    interface Dupable<T> {
        T dup();
    }   
    static class Sheep implements Dupable<Sheep> {
        Sheep(Sheep sheep) { }
        @Override public Sheep dup() {
            return new Sheep(this);
        }
    }
    public static void main(String[] args) {
        Dupable<?> dupable = new Sheep(null);
        System.out.println(dupable);
        System.out.println(dupable.dup());
        
        // no cast needed
        Sheep dolly2 = new Sheep(null).dup();
    }
}

The output should be something like this (as seen on ideone.com):

DupableExample$Sheep@{some hexadecimal code}
DupableExample$Sheep@{a different hexadecimal code, in all likelyhood}

So now given any Dupable<T>, you can invoke T dup() on it to get what you expect is a duplicate copy.

This is just a proof-of-concept: in actual implementation, your copy constructor/copy factory/whatever copy mechanism will actually have the copying logic implemented, and Dupable<T> would be a public top-level interface.


The best idea is to avoid cloning, as it is broken.
Cloneable interface doesn't have method clone. Method clone is defined as protected in Object class. So to call clone you need to know the type of object to have access to clone. Better idea is either copy constructor (as Bloch recommends) or serialization and deserialization (via XML for example).
Maybe you can gain access to clone with reflection, but I'm not sure. And I discourage it.


You can't know for sure that a class has the capacity to clone because clone() is a method from Object. The best you can do is to check if the class is Cloneable. Usually when a class is Cloneable it means that the developer overrode the clone() method.

But even so, Object can't call this method.

There is the reflection solution. But as the doc says :

Even if the clone method is invoked reflectively, there is no guarantee that it will succeed.

You can read documentation here, and there is a statement from Josh Bloch on this class (last paragraph).


You could check whether it implements the Cloneable interface and if it does then use the clone method.

And here is and example on how to implement it yourself.


As others have said: Clonable is broken and you should consider other options such as copy constructors. Having said that, here is a solution that should work if you really must use clone():

Object clone = null;
if(anObj instanceof Clonable) {
    Method cloneMethod = anObj.getClass().getMethod("clone");
    /*
     * TODO: Handle the case where an object is cloneable but 
     * does not have a public clone() method.
     */
    clone = cloneMethod.invoke(anObj);
} else {
    throw new RuntimeException("can't clone object");
}

Or you could use reflection to clone the object field by field, if it does not implement clone()... Get all fields, copy the values to the new object. But this is tricky, if the object has no no-arg constructor.


interface PublicCloneable extends Cloneable{
    public Object clone();
}

class MyObject implements PublicCloneable {
    public Object clone() {
        return super.clone();
    }
}

class MainObject {
    public static void main(String[] params) {
        Object m = new MyObject();

        if (m instanceof PublicCloneable) {
            Object c = m.clone();
        }
    }
}
0

精彩评论

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