Why People Are So Afraid of Using Clone() (On Collection and Jdk Classes)

Why people are so afraid of using clone() (on collection and JDK classes)?

So, give me a reason why not to use clone() with JDK classes?

  • Given an ArrayList reference, you would need a getClass check to check that it is not a subclass of the JDK class. And then what? Potential subclasses cannot be trusted. Presumably a subclass would have different behaviour in some way.
  • It requires that the reference is more specific than List. Some people don't mind that, but the majority opinion is that that is a bad idea.
  • You'll have to deal with a cast, an unsafe cast at that.

How do I avoid .clone()?

The easiest way to return a copy of the array would probably be by calling Arrays.copyOf:

public MyData[] getData() {
return data == null ? null : Arrays.copyOf(data, data.length);
}

If clone( ) for ArrayList is broken why can't they fix it?

The clone() itself is not broken: it's implemented well according to the specification of the clone() method. The problematic place is this specification itself. As it was already discussed in the linked question, there are several drawbacks, namely the necessity to do the unchecked cast, the potential problems which arise if your ArrayList is overridden and the necessity to know that the input List is actually an ArrayList. A good practice for any code requiring the ArrayList is to accept any List (or even Collection) implementation: this way your code becomes more flexible.

As it was already noted, there's a better alternative: using

ArrayList<Type> copy = new ArrayList<>(source);

It's universal: it will work for any source collection and the result will be exactly ArrayList (not derived class). And you should not worry about the performance. According to the implementation it's about the same. See the clone() method code:

public Object clone() {
try {
@SuppressWarnings("unchecked")
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}

So it makes a shallow copy (fast operation of comstant complexity as shallow size is constant and small), then makes single array copy and replaces the mod count. Let's check the constructor:

public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}

It calls the toArray of original collection, uses its result as the internal array for itself and updates the size. It copies the array only in the case if original collection incorrectly returned typed array from the toArray method. What happens if the input collection is also ArrayList? Check the ArrayList.toArray:

public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

See, it's exactly the same operation as in clone() method. Thus in both cases (clone and copy-constructor) you have single Arrays.copyOf(elementData, size) call and small constant overhead.

Explicitly extending Object class and calling clone method of object throwing error

The key thing here is which package the classes belongs to.

This is explained in JLS paragraph 6.6.2:

6.6.2 Details on protected Access

A protected member or constructor of an object may be accessed from outside the package in which it is declared only by code that is responsible for the implementation of that object.


Examples:

This does not compile:

FILE pkg1/A.java (corresponds to the Object class in your question)

package pkg1;
public class A {
protected void method() {};
}

FILE pkg2/B.java (corresponds to storeDate in your question)

package pkg2;
import pkg1.A;
public class B extends A {
public static void main(String args[]) {
new A().method();
}
}

javac outputs the following:

pkg2/B.java:5: method() has protected access in pkg1.A
new A().method();
^

(which is similar to what you have: clone() has protected access in java.lang.Object kkk.clone();)


Simply moving B to the pkg1 package solves it.

That is, this does compile:

FILE pkg1/A.java (unchanged)

package pkg1;
public class A {
protected void method() {};
}

FILE pkg1/B.java (moved from pkg2 to pkg1)

package pkg1;                 // Changed from pkg2
//import pkg1.A; // Not necessary anymore.
public class B extends A {
public static void main(String args[]) {
new A().method();
}
}

So, what would have been required for you to be able to do something like new Object().clone()? Well, you would have to belong to the java.lang package (which in turn, however results in a SecurityException: Prohibited package name: java.lang).

list.clone is prefered over new ArrayList(originalList)

Use new ArrayList(originalList) instead of list.clone() because for list you have to iterate for all object of list and clone individually.

This will work fine for Strings, but it is worth noting that ArrayList.clone will perform a shallow copy, so if there were mutable objects in the list, they will not be cloned and changing one in one list will change that one in the other list as well.

ArrayList shallow copy iterate or clone()

Use clone(), or use the copy-constructor.

The copy-constructor makes additional transformation from the passed collection to array, while the clone() method uses the internal array directly.

Have in mind that clone() returns Object, so you will have to cast to List.

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

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.

clone() vs copy constructor vs factory method?

Basically, clone is broken. Nothing will work with generics easily. If you have something like this (shortened to get the point across):

public class SomeClass<T extends Copyable> {

public T copy(T object) {
return (T) object.copy();
}
}

interface Copyable {
Copyable copy();
}

Then with a compiler warning you can get the job done. Because generics are erased at runtime, something that does a copy is going to have a compiler warning generating cast in it. It is not avoidable in this case.. It is avoidable in some cases (thanks, kb304) but not in all. Consider the case where you have to support a subclass or an unknown class implementing the interface (such as you were iterating through a collection of copyables that didn't necessarily generate the same class).



Related Topics



Leave a reply



Submit