...
This noncompliant code example shows two classes, BadClone and Sub. The class BadClone calls an overridable method doSomething(). The overridden method sets the value of the cookies; the overriding method sets the values of the domain names. The doSomething() method of the subclass Sub is erroneously executed twice at runtime, because of polymorphism. The first invocation comes from BadClone.clone(), and the other comes from Sub.clone(). Consequently, the cookies never get their values initialized, while their domains are initialized twice.
Furthermore, the subclass not only sees the clone in an inconsistent state, but also modifies the clone in a manner that creates inconsistent copies. This is because the deepCopy() method occurs after the call to the doSomething() method and the overriding doSomething() implementation erroneously modifies the object .
| Code Block | ||
|---|---|---|
| ||
class BadCloneCloneExample implements Cloneable { HttpCookie[] cookies; BadCloneCloneExample(HttpCookie[] c) { cookies = c; } public Object clone() throws CloneNotSupportedException { final BadCloneCloneExample clone = (BadCloneCloneExample) super.clone(); clone.doSomething(); // Invokes overridable method clone.cookies = clone.deepCopy(); return clone; } void doSomething() { // Overridable for (int i = 0; i < cookies.length; i++) { cookies[i].setValue("" + i); } } HttpCookie[] deepCopy() { if (cookies == null) { throw new NullPointerException(); } // deep copy HttpCookie[] cookiesCopy = new HttpCookie[cookies.length]; for (int i = 0; i < cookies.length; i++) { // Manually create a copy of each element in array cookiesCopy[i] = (HttpCookie) cookies[i].clone(); } return cookiesCopy; } } class Sub extends BadCloneCloneExample { Sub(HttpCookie[] c) { super(c); } public Object clone() throws CloneNotSupportedException { final Sub clone = (Sub) super.clone(); clone.doSomething(); return clone; } void doSomething() { // Erroneously executed for (int i = 0;i < cookies.length; i++) { cookies[i].setDomain(i + ".foo.com"); } } public static void main(String[] args) throws CloneNotSupportedException { HttpCookie[] hc = new HttpCookie[20]; for (int i = 0 ; i < hc.length; i++){ hc[i] = new HttpCookie("cookie" + i,"" + i); } BadCloneCloneExample bc = new Sub(hc); bc.clone(); } } |
...
This compliant solution declares both the doSomething() and the deepCopy() methods final, preventing overriding of these methods.
| Code Block | ||
|---|---|---|
| ||
class CloneExample implements Cloneable { final void doSomething() { // ... } final HttpCookie[] deepCopy() { // ... } // ... } |
Alternative approaches that prevent invocation of overloaded methods include declaring these methods private, declaring the class final, or eliminating the method calls by congregating the code together.
...