How to clone an Object you dont know the type of?
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 theCloneable
interface does not have aclone
method. And that means it simply doesn't work: making somethingCloneable
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 callingsuper.clone
repeatedly it ends up callingObject
'sclone
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 polymorphicclone
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();
}
}
}
精彩评论