2
class MyCls implements Cloneable {
  @Override
  protected MyCls clone() throws CloneNotSupportedException {
    return new MyCls(//...
  }
}

The above code doesn't have any problem. So why does CopyOnWriteArrayList#clone returns an Object instead of CopyOnWriteArrayList? Compiler cries when casting back from Object to desired type. What might be the reason for this design decision after all? I see this pattern repeated all over the library.

Ralph suggested that the following code is valid instead of above code:

@Override MyCls clone() throws CloneNotSupportedException { 
  MyCls clone = (MyCls)super.clone();
  clone.x = this.x; //or what ever to do return clone
  // ...
}

The question still remains the same. Why does CopyOnWriteArrayList#clone returns Object instead of itself?

  • 1
    Not the question, but: your cloneable method is wrong! Clone must invoke super.clone but must not call a constructor! (this is because: if you want to clone a subclass, then the created instance must be the subclass but not `MyCla`) – Ralph Jul 10 '20 at 06:18
  • @Ralph So does that mean we can get any object as shallow copy of this object? This doesn't make much sense to me. Can you clarify a bit? – Concurrent Bhai Jul 10 '20 at 06:24
  • I recommend to read the Object.clone Javadoc (https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone) and or Effective Java Chapter "Override clone judiciously" https://books.google.de/books?id=ka2VUBqHiWkC&pg=PA54&lpg=PA54 – Ralph Jul 10 '20 at 06:25
  • @Override MyCls clone() throws CloneNotSuppoertedException { MyCls clone = (MyCls)super.clone(); clone.x = this.x; //or what ever to do return clone} – Ralph Jul 10 '20 at 06:26
  • @Ralph That's exactly my point. Why return an `Object` when this can be done: `@Override MyCls clone() throws CloneNotSupportedException { MyCls clone = (MyCls)super.clone(); clone.x = this.x; //or what ever to do return clone}` – Concurrent Bhai Jul 10 '20 at 06:31
  • I understand your question, and I wonder too that `CopyOnWriteArrayList.clone` return `Object` but not `CopyOnWriteArrayList` - but I have no clue. – Ralph Jul 10 '20 at 06:45
  • @Ralph In-fact this pattern is repeated all over the library. There is something that I am unable to recall for a "Aha! Got it" moment. – Concurrent Bhai Jul 10 '20 at 07:10

1 Answers1

6

Your question is based on covariant return types, i.e. the ability to override a method with a more specific return type.

This feature did not always exist in Java. To be more precise, it was introduced in Java 5, the same version that also introduced the concurrency tools, including CopyOnWriteArrayList.

But it’s not as if the language update was developed first and the classes afterwards. The classes went through a development process that started before Java 5 and earlier versions and drafts could be downloaded from http://gee.cs.oswego.edu/dl/concurrency-interest/

So the earlier versions had to declare the same return type as the overridden method and when they were released together with Java 5, nobody thought in time about changing the method to utilize covariant return types. And apparently still nobody in charge considered it. It might not be important enough.

This is not a unique situation.

The NIO Buffer API was introduced with JDK 1.4, one version before covariant return types were possible and it took until Java 9, before overrides with more specific return types were added (e.g. ByteBuffer.position(int), limit(int), or clear(); compare with the Java 8 version having only inherited versions which hindered fluent use of the API).

But mind that changing a method to utilize covariant return types after the class had been published, can create compatibility problems when you compile code under newer JDK versions than it is supposed to run. The solution, the --release option, also was introduced as late as JDK 9.

Holger
  • 285,553
  • 42
  • 434
  • 765