I'm working on some code and trying to figure out how to copy an object reference to another object. I keep seeing the clone() method used, but I've read it's flawed and the class I'm wanting to copy is already implementing serializable. So one question I have is about the difference between using '=' and the clone method - My understanding is that both of these are shallow copies so they should work the same, but if that's true, then what is the benefit of having a method for this?
The code I'm working on has 2 arrays of objects - both the same type of object, all objects in both arrays are initially null, and I assign the values of array B to equal array A.
As the code progresses objects in array A are initialized and values are assigned to the variables of the objects in array A.
At the end of the code though, all objects in array B are still null.
Do I have the concept wrong here?
If the concept isn't wrong I assume it's just something I'm overlooking in my code.
Any help is appreciated.
I'll make my comment an answer:
Your question is comparing apples to oranges, they are so completely different that they can't be compared. = assigns a reference, that's it. Clone creates a completely new object, one whose state should be the same as the cloned object, but again it is a completely different object/reference. As an aside, there are deep and shallow clones, and so the composite fields of shallow copied clones may be identical, but that's the subject of another question.
When you use =, you copy the reference of the object (in memory). When using .clone(), you create a new object.
Related
I'm having an odd problem that I haven't encountered before with copying objects in Java.
So, I've written a class in my code called "State". This contains a few ints, a 2d array, a string and such...
So, for an instance of State called S, I want to make a copy of it called X (I do this simply by writing State X = S; ). Then I want to make changes to X, do some evaluations based on those changes and then just throw away X and keep using S. However, the problem I'm getting is that S seems to be getting the changes that I make to X.
This seems odd to me, since I feel quite certain that I've done things like this before but never had this problem.
Any thoughts?
(Thanks in advance)
I want to make a copy of it called X (I do this simply by writing State X = S; ).
That does not make a copy of the object.
Variables (of non-primitive types) in Java are references - they are not the objects themselves. By doing
State X = S;
you are not copying an object, you are just copying the reference - the result is that you now have two variables that are referring to the same object. If you modify the object through one reference, you'll see the changes also through the other reference.
One way to copy objects is by using the clone() method. For this to work, the class of the object that you are trying to copy must implement interface Cloneable. Another (and probably better) way is to create a copy constructor, and use it to copy the object:
public class State {
public State(State other) {
// initialize this object by copying content from other
}
}
// Make a copy
State X = new State(S);
Your code is not creating a copy of the object. What you are doing there is creating a new reference and pointing it to the same object.
Search for "how to clone an object in Java". Read up on the Cloneable interface.
int[][] array = new int[][] {...}
int[][] clone = array.clone();
I naively expected this to work. But it didn't - it cloned only the first dimension, and I had to go and clone the other dimension manually if I wanted a true clone. Note: the contents were properly copied. But when I changed clone[0][1], it reflected in array[0][1]
And while .clone() is known to perform a shallow clone, int[][] looks like a single object (if we don't know its internal implementation, at least)
Why is that behaviour chosen? Isn't int[][] referencing an array object, rather than just the first dimension of the array? And in what scenarios is cloning only the first dimension the desired behaviour?
Why is that behaviour chosen?
Consistency, most likely.
As you say, int[][] references an array object. It just so happens that the contents of each array element is another array, but that's just a detail. Java clones all arrays identically, and since the elements can be of any type it can't guarantee to perform a deep copy.
Hence clone() for arrays performs a shallow copy, so only the first dimension is cloned.
(In general it doesn't seem that there's a single "best" or "obvious" answer to the question of whether a clone implies deep or shallow copies. What the developer wants will depend on how each field is being used by the application, so a one-size-fits-all approach will naturally have limitations.)
This behavior is demonstrated because there is no true multi dimensional array.
Java achieves multiple dimensions by making arrays of arrays. Which means:
int[][] is actually Array<Array<int>>
Hence the clone would only copy the first dimension.
If you were trying with a 3 dimensional array, you would need to clone thrice.
The clone method is a so called shallow copy (see another stackoverflow answer on the clone method), which means that all the elements are copied by reference.
To get to what you want (also called deep cloning) you can either copy every single array yourself using the Arrays.copy recursively, so every (sub-)array you find will get a deep-clone, or check this stackoverflow answer on deep cloning.
Happy hacking :-)
I am actually not able to replicate the behavior you have mentioned. I can see some answers mentioning shallow copy as the root cause of the issue. Correct me if I am wrong, shallow copy just means that instead of copy of an object while cloning, we will get copy of the reference. A good explanation can be found here In Java, what is a shallow copy?
In this case shallow copy will only make sure that changes in original array get reflected in the cloned array but will not stop anything from being copied.
int[][] array = new int[][] {{1,2,3},{4,5,6}, {7,8,9}};
int[][] clone = array.clone();
//Try this to see magic of shallow copy
//array[2][1]=11;
for(int i=0;i<array.length;i++)
for(int j=0;j<array[i].length;j++)
System.out.println("array["+i+"]["+j+"]"+array[i][j]);
for(int i=0;i<clone.length;i++)
for(int j=0;j<clone[i].length;j++)
System.out.println("clone["+i+"]["+j+"]"+clone[i][j]);
For me the cloning is done perfectly, that is both arrays have same contents. The only reason I can think of for getting the issue that cloned array will not show some information is that we have actually modified original array after the cloning (shallow copy comes into play then).
BTW I used Java 1.6, I hope that is not an issue.
Edit: If we just need to understand why their is a shallow copy instead of deep copy of a multidimensional array. Let's look at 2 facts
While cloning, Objects always gets cloned as shallow copy (copy of reference of the object), only native types get a real copy. In Java, what is a shallow copy?
An array in java is actually an object Is an array an object in java
Now combining 1 and 2, we know that multidimensional array in Java is just an object, which has reference of other array objects.
Something like ArrayObj->{ArrayObj, ArrayObj, ArrayObj};
And because we have Objects inside objects (composition), we are supposed to get a shallow copy as per Java cloning rule.
I have encountered a problem in one of my Java projects, which causes bugs.
The problem sounds as following:
I have two arrays. Let's name them firstArray and secondArray. Object in this case is a seperate class created by me. It works, the array can be filled with objects of that type.
Object[] firstArray= new Object[];
Object[] secondArray = new Object[];
Now, when I get an element out of the first array, edit it and then copy it in the second array, the object from the first array gets altered too.
tempObj = firstArray[3];
tempObj.modifySomething();
secondArray[3] = tempObj;
Whenever I do this, the (in this case) 3rd element(actually 4th) of the first array gets the modifications. I don't want this. I want the first Array to remain intact, unmodified, and the objects I have extracted from the first array and then modified should be stored in the second so that the second array is actually the first array after some code has been run.
P.S. Even if I get the element from the first array with Array.get(Array, index) and then modify it, the element still gets modified in the first array.
Hopefully you understood what I wanted to say, and if so, please lend me a hand :)
Thank you!
You're going to have to create a new object.
The problem is the modifySomething call. When you do that, it alters the object on which it's called. So if you've only got one object (even by two names), you can't call modifySomething or they will both change.
When you say secondArray[3] = firstArray[3], you aren't creating a new object: you're just assigning a reference. Going through an intermediate temporary reference doesn't change that.
You'll need code that looks like this:
Object tempObj = firstArray[3].clone();
tempObj.modifySomething();
secondArray[3] = tempObj;
The clone() method must return a new object divorced from the original but having identical properties.
When you retrieve an element from your array, you have a reference to it. So if you modify it, the modification are shered through all the object's references.
In order to leave it intact, you should use some method like Object.clone() or create a new Object and use its constructor to initialize its fields.
The object extracted from the first array needs to be cloned to create a new instance that is seperate. Otherwise the modification will affect the object in the first array as it is the same object.
When you retrieve an element from your array, you get a reference to it. So if you modify it, the modification are shared through all the object's references.
In order to leave it intact, you should use some method like Object.clone() or create a new method which take in input your retrieved object and return a new one alike.
In Java, when you do this secondArray[3] = tempObj;, you actually put the reference to the array, not the real object
So firstArray[3] and secondArray[3] point to the same real object
What you need to do is to create a new object that is identical to your original object, and put the reference of the new object to your secondArray
It might worth to point out that default clone() function only does a shallow copy, so if you have mutable objects in your object's fields, it might cause some problems. Take a look at this article about how to do a deep copy
When adding an object to a data structure the reference to the object is always added, not the object itself. So below code snippets are fundamentally the same ?
Snippet 1 -
Object objectRef = new Object();
Vector vector = new Vector();
vector.add(objectRef);
Object object = (Object)vector.get(0);
Snippet 2 -
Vector vector = new Vector();
vector.add(new Object());
Object object = (Object)vector.get(0);
Java always passes references by value. So whenever you use a reference as an argument to a function call, the reference value is copied. The object itself is not passed, a copy of its reference is. Its an important distinction to understand. It also is important to understand that with the reference you (or the library) can still operate on the object passed via its reference.
Your snippets are not entirely the same. In (1) you end up with two references to the same object, objectRef and object. In (2) you end up with only 1, object. In both cases the vector has a different reference to the object.
Both snippets are the same, the only difference is that you have a location variable named objectRef which can be modified after it was added to the vector, if it is modified, it will be reflected in the object returned from the vector as well since it IS the same object. Though the "Object" class doesn't exactly have any methods which can modify the object.
That's correct. In the java programming language there is only heap. Every object exists out there in magic heap land. Every non-primitive value is just a pointer whose only operation is to dereference. A List can be thought of as a list of pointers to strings, the actual data content of the strings is out on the heap, not inside the collection structure.
(This is the semantics of the language of course. Modern runtimes are under no obligation to actually do it that way, the JIT uses method stacks, it just has to behave exactly the same as if it worked that way.)
They result in the same state except that at the end of snippet 2, you don't have a symbol called objectRef.
Both snippets give the same results. The difference lies that in the first snippet you have three references to the same object (objectRef, object and a reference in vector), and in the second snippet you only have two references to the same object (object and a reference in vector).
I understand that in order to copy an arraylist and have two lists independent of one another you must use a deep copy (copying the objects from one list to another not just the references), however is there a way this can be done cross-class?
For example; I am calling Class2 from Class1. In Class2 objects are added to an ArrayList of custom objects upon an event. I would like to be able to transfer this ArrayList to Class1 but whenever I try I get a NullPointer.
Any clues??
This is highly indicative of a design flaw.
See if you can't accomplish the same goal by wrapping your list in a class, sharing the class and using it to control access to the list.
The only case where this wouldn't just outright work is if your two classes must modify the list independently.
If this is a requirement, then I would probably hand a different instance of the wrapper class to each modifying class (with a reference to the same source list), then have a way for newly added data to be tagged with an ID referring to the original class--that way when you query, the wrapper would only return untagged items (items that were part of the original shared list) and items tagged with it's own ID.
Either that or the wrapper class could contain a second list and when queried, return the combined results of the original and second lists.
I've almost never wanted a deep copy. It happens, but it's quite rare.
If you post more info, maybe we can be more specific in helping with the redesign.
ALSO: (Edit)
Chances are that the copied array list isn't your problem--it's probably that it wasn't TRULY a deep copy. For a deep copy, it means you implement a copy method (I believe they are supposed to be called .clone(), I never use this stuff--as I said, it's bad juju) for each object in the array list, then you call your copy method on each one to get the new copy in your next list.
Furthermore, any objects referenced by your copied object should probably be cloned as well. "Deep" means all the way down the tree.
I'm guessing you're failing somewhere in the process.
I'd really like to hear why you feel you need a copy instead of a reference.
My suggestion is for you to create a getArray() method and call it from the other class. This method should create a copy of the ArrayList because you should not "transfer" variables within classes; always with get() method so OO paradigm stays intact.
Do something like this:
Class 1
public ArrayList<Object> getArray() {
ArrayList<Object> aux = new ArrayList<Object>();
for(Object x : list) //object is the string, int, etc...
aux.add(x.clone()) //assuming the Object has a clone method!
return aux;
}
On Class 2, just call this method. Then just look at the test from the other answer about the null exception, should work.
Hope it helps.