...
Code Block | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
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 specifiedObject
. This method is equivalent to:
compareTo(when) > 0
if and only ifwhen
is aCalendar
instance. Otherwise, the method returnsfalse
.
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 | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
// 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