Calling Superclass Constructors


An initial noncompiling attempt at an implementation for SummerCourseSession follows.

 // this code does not compile! package sis.summer; import java.util.*; import sis.studentinfo.*; public class SummerCourseSession extends CourseSession {    public static SummerCourseSession create(          String department,          String number,          Date startDate) {       return new SummerCourseSession(department, number, startDate);    }    private SummerCourseSession(          String department,          String number,          Date startDate) {       super(department, number, startDate);    }    Date getEndDate() {       GregorianCalendar calendar = new GregorianCalendar();       calendar.setTime(startDate);       int sessionLength = 8;       int daysInWeek = 7;       int daysFromFridayToMonday = 3;       int numberOfDays =          sessionLength * daysInWeek - daysFromFridayToMonday;       calendar.add(Calendar.DAY_OF_YEAR, numberOfDays);       return calendar.getTime();    } } 

The key things to note are in bold.

First, you want to declare SummerCourseSession as a subclass of CourseSession, so you use the extends keyword in the class declaration.

Second, the constructor for SummerCourseSession makes a call to the superclass constructor that takes the department, course number, and start date as parameters. It does this by using the super keyword. This is similar to a super method invocation.

Third, in order to quickly get things working, you copied the getEndDate method from CourseSession and altered the value of the sessionLength temporary to be 8 instead of 16.

This code will not compile. You should see three compiler errors. The first one you know how to fix:

 getEndDate() is not public in sis.studentinfo.CourseSession; cannot be accessed from outside package       assertEquals(eightWeeksOut, session.getEndDate());                                          ^ 

Change the definition of getEndDate in CourseSession to public and recompile:

 public Date getEndDate() { 

You now receive a different error related to getEndDate:

 getEndDate() in sis.summer.SummerCourseSession cannot override getEndDate() in sis.studentinfo.CourseSession; attempting to assign weaker access privileges; was public    Date getEndDate() {         ^ 

A subclass method that overrides a superclass method must have equivalent or looser access privileges than a superclass method. In other words, CourseSession's control over the getEndDate method must be tighter than the control of any of its subclasses. If a superclass marks a method as package, a subclass can mark the overridden method as package or public. If a superclass marks a method as public, a subclass must mark the overridden method as public.

The solution is to change getEndDate in SummerCourseSession to be public:

 public Date getEndDate() { 

After compiling, you should have two remaining errors that appear similar:

 CourseSession(java.util.Date) has private access in sis.studentinfo.CourseSession       super(department, number, startDate);       ^ startDate has private access in sis.studentinfo.CourseSession       calendar.setTime(startDate);                        ^ 

Sure enough, the CourseSession constructor that takes a Date as a parameter is marked private:

 private CourseSession(    String department, String number, Date startDate) {    // ... 

You made the constructor private in order to force clients to construct CourseSession instances using the class creation method. The problem is, the private access modifier restricts access to the constructor. Only code defined within CourseSession itself can invoke its constructor.

Your intent is to expose the CourseSession constructor to code only in CourseSession itself or code in any of its subclasses. Even if you define a CourseSession subclass in a different package, it should have access to the CourseSession constructor. You don't want to expose the constructor to other classes.

Specifying public access would allow too much access. Non-subclasses in different packages would be able to directly construct CourseSession objects. Package access would not allow subclasses in different packages to access the CourseSession constructor.

Java provides a fourth (and final) access modifier, protected. The protected keyword indicates that a method or field may be accessed by the class in which it is defined, by any other class in the same package, or by any of its subclasses. A subclass has access to anything in its superclass marked as protected, even if you define the subclass in a package other than the superclass.

Change the CourseSession constructor from private to protected:

 protected CourseSession(    String department, String number, Date startDate) {    // ... 

The compilation error regarding the constructor goes away. You have one remaining error: getEndDate references the private superclass field startDate directly.

 startDate has private access in sis.studentinfo.CourseSession       calendar.setTime(startDate);                        ^ 

You could solve the problem by changing startDate to be a protected field. A better solution is to require that subclasses, like any other class, access the field by using a method.

Change the getStartDate method from package to protected access.

 public class CourseSession implements Comparable<CourseSession> {    ...    protected Date getStartDate() {       return startDate;    }    ... 

You must change the code in both CourseSession and SummerCourseSession to use this getter:

 // CourseSession.java public Date getEndDate() {    GregorianCalendar calendar = new GregorianCalendar();     calendar.setTime(getStartDate());     final int sessionLength = 16;     final int daysInWeek = 7;     final int daysFromFridayToMonday = 3;     int numberOfDays =        sessionLength * daysInWeek - daysFromFridayToMonday;     calendar.add(Calendar.DAY_OF_YEAR, numberOfDays);     return calendar.getTime(); } // SummerCourseSession.java: public Date getEndDate() {    GregorianCalendar calendar = new GregorianCalendar();    calendar.setTime(getStartDate());    int sessionLength = 8;    int daysInWeek = 7;    int daysFromFridayToMonday = 3;    int numberOfDays =       sessionLength * daysInWeek - daysFromFridayToMonday;    calendar.add(Calendar.DAY_OF_YEAR, numberOfDays);    return calendar.getTime(); } 

Since I told you to be lazy and copy the code for getEndDate, note that you had the additional effort of making the change in two places.

In SummerCourseSession, getEndDate makes a call to getStartDate, but getStartDate is not defined in SummerCourseSession. Since the super. scope modifier was not used, Java first looks to see if getStartDate is defined in SummerCourseSession. It is not, so Java starts searching up the inheritance chain until it finds an accessible definition for getStartDate.

Protected-level access is one step "looser" than package. One minor downside is that it is possible for other classes in the same package to access a protected element (method or field). Usually this is not what you want. There are few good design reasons to make a field accessible to other classes in the same package and to subclasses in different packages but not to other classes.

In the case of CourseSession, you want the constructor to be accessible only to subclasses, regardless of package, and not to any other classes. There is no way in Java to enforce this restriction.



Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net