...
Code Block | ||
---|---|---|
| ||
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 } |
The {{ Wiki Markup 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 thisCalendar
represents a time after the time represented by the specifiedObject
. This method is equivalent to
compareTo(when) > 0
if and only ifwhen
is aCalendar
instance. Otherwise, the method returnsfalse
.
...
Compliant Solution (Calendar
)
This 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 Wiki Markup 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 | ||
---|---|---|
| ||
// 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] | |||||
Item 16. Favor composition over inheritance | |||||
Design Patterns, Elements of Reusable Object-Oriented Software | |||||
<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 | 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