Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: clarified NCCE

...

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
bgColor#FFcccc
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
bgColor#ccccff

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.

...