Clockwork-Muse
Clockwork-Muse

Reputation: 13056

Accessing a final variable before constructor exit

Okay, I've been messing around with the excellent JodaTime library, attempting to get a general-case retail/fiscal (4-5-4) calendar implemented. I've already found the specific case for my company, but the general cases (mostly in determining start-of-year, and leap years) is killer; for instance, there's a set of dates whereby two fiscal years (364 days long, normally) will start during 1 ISO year.

In the process of determining year-start rules, I ended up with an abstract class and a couple of concrete classes for determining year starts, based on which side of the ISO leap-day they fall.

(pared-down) abstract class:

private static abstract class SimpleFiscalYearEndPattern implements FiscalYearEndPattern {

    protected final int leapYearCountOffset;
    protected final int doomsdayOffset;

    private final int startingDayOfWeek;
    private final int yearOffset;
    private final long millisFromEpochToFiscalYearStart;
    private final long millisElapsedToEpochDividedByTwo;

    /**
     * Restricted constructor
     * @param fiscalYear
     * @param startingOn
     * @param inFirstWeek
     */
    protected SimpleFiscalYearEndPattern(final int fiscalYear, final LocalDate startingOn, final MonthDay inFirstWeek) {
        this.yearOffset = fiscalYear - startingOn.getYear();
        this.doomsdayOffset = getDoomsdayOffset(inFirstWeek);
        this.startingDayOfWeek = startingOn.getDayOfWeek();

        final int startingDoomsday = getDoomsdayOffset(new MonthDay(startingOn, REFERENCE_CHRONOLOGY));
        // If the starting doomsday is a later day-of-week, it needs to become negative.
        this.leapYearCountOffset = calculateLeapYearCountOffset(startingDoomsday : doomsdayOffset, doomsdayOffset);

        final int leapYearsBefore = getPreviousLeapYears(fiscalYearBeforeEpoch);
    }
}

(pared down) concrete class (for dates in range 1/7 - 2/28):

private static final class BeforeLeapYearEndPattern extends SimpleFiscalYearEndPattern {

    private static final int FIRST_YEAR_LEAP_YEAR_OFFSET = -1;

    private BeforeLeapYearEndPattern(final int fiscalYear, final LocalDate startingOn, final MonthDay onOrBefore) {
        super(fiscalYear, startingOn, onOrBefore);
    }

    public static final BeforeLeapYearEndPattern create(final int fiscalYear, final LocalDate startingOn, final MonthDay onOrBefore) {
        return new BeforeLeapYearEndPattern(fiscalYear, startingOn, onOrBefore);
    }

    /* (non-Javadoc)
     * @see ext.site.time.chrono.FiscalYearEndPatternBuilder.SimpleFiscalYearEndPattern#getPreviousLeapYears(int)
     */
    @Override
    protected int getPreviousLeapYears(final int isoYear) {
        // Formula gets count of leap years, including current, so subtract a year first.
        final int previousYear = isoYear - 1;
        // If the doomsday offset is -1, then the first year is a leap year.
        return (previousYear + leapYearCountOffset + (previousYear / 4) - (previousYear / 100) + (previousYear / 400)) / 7 + (leapYearCountOffset == FIRST_YEAR_LEAP_YEAR_OFFSET ? 1 : 0);
    }

If you'll notice, I use leapYearCountOffset, which is defined (As a final variable) in the abstract super class, in getPreviousLeapYears(), which is then called from the super-class constructor. I don't want to repeat the formula in the super-class constructor - it's different for dates in the range 3/1-12/31; nor do I want to place the instance variable in the concrete subclass - the other calculation still needs leapYearCountOffset.

The Question Is: What is the state of leapYearCountOffset at the time the (subclass) method is called from the constructor? Is it guaranteed in any way, or is that something that could change at the whim of the compiler? And how the heck could I test that to find out? I'm already aware that the compiler is free to re-arrange some statements, but would (could?) that happen here?

Upvotes: 3

Views: 237

Answers (4)

Mike Samuel
Mike Samuel

Reputation: 120506

Since the getPreviousLeapYears is called after leapYearCountOffset is assigned, leapYearCountOffset will be properly initialized and getPreviousLeapYears will see the correct value.


Java leaves the burden of ensuring that final variables that are accessed by code called during the constructor are properly initialized before first access. If not properly initialized, code called during the constructor will see the zero value for that field's type.

The program

public class Foo {
  protected final int x;
  Foo() {
    foo();
    this.x = 1;
    foo();
  }
  void foo() { System.out.println(this.x); }
  public static void main(String[] argv) { new Foo(); }
}

prints

0
1

since x is not initialized during the first call to foo, but as explained above, you don't have this problem.


The JLS says that every use of a final in the constructor must be after initialization but makes no such guarantee for other methods. Consider

abstract class C {
  public final int x;

  C() {
    this.x = f();
  }

  abstract int f();
}

For the language to ensure that x is initialized before every use, it would need to ensure that no subclass exists like

class D extends C {
  int f() {
    return this.x;
  }
}

This requires global reasoning about classes which would be at odds with Java's dynamic linking and add a lot of complexity to the language specification.

Upvotes: 2

axtavt
axtavt

Reputation: 242686

It looks like this question is caused by confusion between intrathread and interthread semantics.

As long as you run the code in question in a single thread everything works as you would expect: there can be no visible effects of code reordering.

The same is true for final fields. final fields provide additional guarantees for concurrent access, and these guarantees only take effect after completion of constructors. That's why it's not recommended to make final fields accessible to other threads before completion of constructor. But it doesn't matter as long as you don't try to access the field in question from other threads.

However, I agree that calling subclass methods from constuctor of a superclass is a bad practice because subclass fields are not initialized at that point.

Upvotes: 0

Joni
Joni

Reputation: 111239

While leapYearCountOffset is guaranteed to have its final value, this is still an accident waiting to happen. The method getPreviousLeapYears is executed before the subclass initialization has started, so any variables from the subclass will have their default values (0 or null).

Right now there is no danger, but if someone comes in and changes BeforeLeapYearEndPattern, maybe by adding a new final instance variable, which is then used in getPreviousLeapYears, you will get hurt.

Upvotes: 0

stevevls
stevevls

Reputation: 10843

One of the guarantees of final variables is that the compiler won't let you access them before they're assigned. So, if it compiles (which it should), you're good to go!

Upvotes: 4

Related Questions