Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

...

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) {
      return true;
    }
    return super.after(when);
  }

  @Override public int compareTo(Calendar anotherCalendar) {
    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();
    cs1.setTime(new Date());
    // Date of last Sunday (before now)
    cs1.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
    // Wed Dec 31 19:00:00 EST 1969
    CalendarSubclass cs2 = new CalendarSubclass();
    // expected to print true
    System.out.println(cs1.after(cs2));
  }

  // Implementation of other Calendar abstract methods
}

Wiki MarkupThe {{java.util.Calendar}} class provides a {{compareTo()}} method and an {{after()}} method. The {{after()}} method is documented in the _Java API Reference_ \[ [API 2006|AA. References#API 06] \] as follows:

The after() method 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.

...

Compliant Solution (Calendar)

Wiki MarkupThis compliant solution uses a design pattern called composition and forwarding (sometimes also called delegation) \[[Lieberman 1986|AA. References#Lieberman 86]\], \[[Gamma 1995|AA. References#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 This compliant solution uses a design pattern called composition and forwarding (sometimes also called delegation) [Lieberman 1986], [Gamma 1995, 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 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;
    }
    // Does not compare with first day of week any longer;
    // Uses default comparison with epoch
    return super.after(when); 
  }

  @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();
    ci1.setTime(new Date());
    // Date of last Sunday (before now)
    ci1.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);

    CalendarImplementation ci2 = new CalendarImplementation();
    CompositeCalendar c = new CompositeCalendar(ci1);
    // expected to print true
    System.out.println(c.after(ci2));
  }
}

...

Secure Coding Guidelines for the Java Programming Language, Version 3.0

Guideline 1-3. Understand how a superclass can affect subclass behavior

Bibliography

[API 2006]

Class Calendar

[Bloch 2008]

Item 16. Favor composition over inheritance

[Gamma 1995]

Design Patterns, Elements of Reusable Object-Oriented Software

[Lieberman 1986

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="f0058ae0-ea54-4f78-8e6c-d77fe4643ebf"><ac:plain-text-body><![CDATA[

[[API 2006

AA. References#API 06]]

[Class Calendar

http://download.oracle.com/javase/6/docs/api/java/util/Calendar.html]

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="3228319e-9b83-4d8d-af6a-70a255d2fcdf"><ac:plain-text-body><![CDATA[

[[Bloch 2008

AA. References#Bloch 08]]

Item 16. Favor composition over inheritance

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="61b83fc6-c4ce-411f-aeb8-b290a53f9dc3"><ac:plain-text-body><![CDATA[

[[Gamma 1995

AA. References#Gamma 95]]

Design Patterns, Elements of Reusable Object-Oriented Software

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="b68159f0-ca83-46d5-8b25-9b575a26da53"><ac:plain-text-body><![CDATA[

[[Lieberman 1986

AA. References#Lieberman 86]]

Using prototypical objects to implement shared behavior in object-oriented systems ]]></ac:plain-text-body></ac:structured-macro>

...

OBJ01-J. Declare data members as private and provide accessible wrapper methods      04. Object Orientation (OBJ)      OBJ03-J. Do not mix generic with nongeneric raw types in new code