Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: overhauled 2nd NCCE/CS, prob not done

...

Code Block
bgColor#FFCCCC
public class Client {
  public static void main(String[] args) {
    SuperClass sc = new SubClass(); // Override
    
    if (sc.withdraw(200.0)) {        // Validate and enforce security manager check 
      SubClass.doLogic(sc, 200.0);  // Withdraw 200.0 from superclass
    } else {
      sc.overdraft(); // Newly added method, lacks security manager checks 
    }
  }
}

...

Alternatively, when the intended design permits the new method in the parent class to be invoked directly from a subclass without overriding, install a security manager check directly in the new method.

Noncompliant Code Example (Calendar)

This noncompliant code example overrides the methods after() and compareTo() of the class java.util.Calendar. The Calendar.after() method returns a boolean value that indicates whether the Calendar represents a time after that represented by the specified Object parameter. The programmer wishes to extend this functionality so that the after() method returns true even when the two objects represent the same date. She also overrides the method compareTo() to provide a "comparisons by day" option to clients. For example, comparing today's day with the first day of week (which differs from country to country) to check whether it is a weekday.

Code Block
bgColor#FFCCCC
class CalendarSubclass extends Calendar {
  @Override public boolean after(Object when) {
    // correctly calls Calendar.compareTo()
    if (when instanceof Calendar && super.compareTo((Calendar) when) == 0) { // Note the == operator
      return true;
    }
    return super.after(when); // Calls CalendarSubclass.compareTo() instead of Calendar.compareTo() 
  }
	
  @Override public int compareTo(Calendar anotherCalendar) {
    // This method is erroneously invoked by Calendar.after()
    return compareDays(this.getFirstDayOfWeek(), anotherCalendar.getFirstDayOfWeek())return compareDays(this.getFirstDayOfWeek(), anotherCalendar.getFirstDayOfWeek());
  }

  private int compareDays(int currentFirstDayOfWeek, int anotherFirstDayOfWeek) {
    return (currentFirstDayOfWeek > anotherFirstDayOfWeek) ?
           1 : (currentFirstDayOfWeek == anotherFirstDayOfWeek) ? 0 : -1;
  }

  public static void main(String[] args) {
    CalendarSubclass cs1 = new CalendarSubclass(); 
    CalendarSubclass cs2 = cs1.setTime(new CalendarSubclassDate());  // Wed Dec 31 19:00:00 EST 1969
    cs1.setTime(new Date());          
    cs1.set( Calendar.DAY_OF_WEEK, Calendar.SUNDAY); // Date of last Sunday  // Current day's date 
    System.out.println(cs1.after(cs2))(before now)
    CalendarSubclass cs2 = new CalendarSubclass();   // Wed Dec 31 19:00:00 EST 1969
    System.out.println(cs1.after(cs2));              // expected printsto print falsetrue
  }

  // Implementation of other Calendar abstract methods 
}

// The implementation of java.util.Calendar.after() method is shown below

Such errors generally occur because the developer has depended on assumptions about the implementation specific details of the superclass. Even when these assumptions are correct when originally made, the implementation details of the superclass may change in the future without warning.

Wiki Markup
The {{java.util.Calendar}} class provides a {{compareTo()}} method, and an {{after()}} method. The {{after()}} method is documented as follows: (\[[API 2006|AA. Bibliography#API 06]\])

Returns whether this Calendar represents a time after the time represented by the specified Object. This method is equivalent to:
compareTo(when) > 0
if and only if when is a Calendar instance. Otherwise, the method returns false.

The documentation does not state if after() actually invokes compareTo() or if compareTo() invokes after(). In the Oracle JDK 1.6 implementation, the source code for after() is as follows:

Code Block
bgColor#ccccff

  public boolean after(Object when) {
  return when instanceof Calendar && compareTo((Calendar)when) > 0; // Note the > operator
 when) {
    return when instanceof Calendar
      // forwards to the subclass's implementation erroneously
}
&& compareTo((Calendar) when) > 0;

Such errors generally occur because the developer has depended on assumptions about the implementation specific details of the superclass. Even when these assumptions are correct when originally made, the implementation details of the superclass may change in the future without warning. In this case, the two objects are initially compared using the overriding CalendarSubclass.after() method; subsequently, . This invokes the superclass's Calendar.after() method is explicitly invoked to perform the remainder of the comparison. But the superclass Calendar's .after() method internally uses class Object's calls the compareTo() method, which is delegated to CalendarSubclass.compareTo() method. Consequently, the superclass's CalendarSubclass.after() method invokes the subclass's version of actually calls CalendarSubclass.compareTo(), and consequently returns false.

Because the developer of the subclass was unaware of the implementation details of the superclass's implementation of Calendar.after(), she incorrectly assumed that the superclass's after() method would invoke only its own methods without invoking overriding methods from the subclass. The guideline MET04-J. Ensure that constructors do not call overridable methods describes similar programming errors.

Compliant Solution (Calendar)

Wiki Markup
This compliant solution recommends the use ofuses a design pattern called composition and forwarding (sometimes also referred to as delegation) \[[Lieberman 1986|AA. Bibliography#Lieberman 86]\] and \[[Gamma 1995|AA. Bibliography#Gamma 95, p. 20]\]. The compliant solution introduces a new _forwarder_ class that contains a {{private}} member field of the {{Calendar}} type; this is _composition_ rather than inheritance. In this example, the field refers to {{CalendarImplementation}}, a concrete instantiable implementation of the {{abstract}} {{Calendar}} class. The compliant solution also introduces a wrapper class called {{CompositeCalendar}} that provides the same overridden methods found in the {{CalendarSubclass}} from the preceding noncompliant code example.

Code Block
bgColor#ccccff
// The CalendarImplementation object is a concrete implementation of the abstract Calendar class
// Class ForwardingCalendar
public class ForwardingCalendar {
  private final CalendarImplementation c;

  public ForwardingCalendar(CalendarImplementation c) {
    this.c = c;
  }

  CalendarImplementation getCalendarImplementation() {
    return c;
  }

  public boolean after(Object when) {
    return c.after(when);
  }

  public int compareTo(Calendar anotherCalendar) {
    // CalendarImplementation.compareTo() will be called
    return c.compareTo(anotherCalendar);
  }
}

//Class CompositeCalendar

class CompositeCalendar extends ForwardingCalendar {
  public CompositeCalendar(CalendarImplementation ci) {
    super(ci);  
  }
  
  @Override public boolean after(Object when) {
    // This will call the overridden version i.e. CompositeClass.compareTo();
    if (when instanceof Calendar && super.compareTo((Calendar)when) == 0) {
      // Return true if it is the first day of week
      return true;
    }
    return super.after(when); // Does not compare with first day of week anymore;
                              // Uses default comparison with epoch
  }
	
  @Override public int compareTo(Calendar anotherCalendar) {
    return compareDays(super.getCalendarImplementation().getFirstDayOfWeek(),
                       anotherCalendar.getFirstDayOfWeek());
  }

  private int compareDays(int currentFirstDayOfWeek, int anotherFirstDayOfWeek) {
    return (currentFirstDayOfWeek > anotherFirstDayOfWeek) ?
           1 : (currentFirstDayOfWeek == anotherFirstDayOfWeek) ? 0 : -1;
  }

  public static void main(String[] args) {
    CalendarImplementation ci1 = new CalendarImplementation();
    CalendarImplementation ci2 = new CalendarImplementation();
ci1.setTime(new Date());
    ci1.set( Calendar.DAY_OF_WEEK, Calendar.SUNDAY); // Date of last Sunday (before now)

    CompositeCalendarCalendarImplementation cci2 = new CompositeCalendarCalendarImplementation(ci1);
    CompositeCalendar c = ci1.setTime(new DateCompositeCalendar()ci1);
    System.out.println(c.after(ci2));                // expected to printsprint true 
  }
}

Note that each method of the class ForwardingCalendar redirects to methods of the contained CalendarImplementation class instance (CalendarImplementation), from which it receives return values; this is the forwarding mechanism. The ForwardingCalendar class is largely independent of the implementation of the class CalendarImplementation. Consequently, future changes to CalendarImplementation are unlikely to break ForwardingCalendar and thus are also unlikely to break CompositeCalendar, which derives from ForwardingCalendar. Invocations of CompositeCalendar's overriding after() method perform the necessary comparison by using the local version of the CalendarImplementation.compareTo() method as required. Using super.after(when) forwards to ForwardingCalendar which invokes the CalendarImplementation's .after() method as required. Consequently, ava.util.Calendar.after() invokes the CalendarImplementation's .compareTo() method rather than the overriding version in CompositeClass that was inappropriately called in the noncompliant code exampleas required, with the result that the program correctly prints true.

Related Vulnerability: JDK 1.2 java.util.Hashtable.entrySet()

...

Bibliography

Wiki Markup
\[[SCGAPI 20072006|AA. Bibliography#SCGBibliography#API 0706]\] Guideline 1-3 Understand how a superclass can affect subclass behavior[Calendar|http://download.oracle.com/javase/6/docs/api/java/util/Calendar.html]
\[[Bloch 2008|AA. Bibliography#Bloch 08]\] Item 16: "Favor composition over inheritance"
\[[Gamma 1995|AA. Bibliography#Gamma 95]\] 
\[[Lieberman 1986|AA. Bibliography#Lieberman 86]\]
\[[SCG 2007|AA. Bibliography#SCG 07]\] Guideline 1-3 Understand how a superclass can affect subclass behavior

...

OBJ06-J. Compare classes and not class names      04. Object Orientation (OBJ)      MET18-J. Avoid using finalizers