Access to the final variable before exiting the constructor

Ok, I was busy with the excellent JodaTime library trying to implement a retail / fiscal (4-5-4) general business calendar. I have already found a specific case for my company, but the general cases (mainly in determining the beginning of the year and leap years) are a killer; for example, there is a set of dates when two years (usually 364 days, usually) begin within one year of ISO.

In the process of defining the rules of the beginning of the year, I ended up with an abstract class and several specific classes to determine the beginning of the year, based on which side of the ISO lipday they fall.

(abbreviated) 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);
    }
}

(reduced) specific class (for dates in the 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);
    }

, leapYearCountOffset, ( ) , getPreviousLeapYears(), . - ​​ 3/1-12/31; - - leapYearCountOffset.

:. leapYearCountOffset () ? - , , ? , , , ? , , (?), ?

+3
4

getPreviousLeapYears leapYearCountOffset, leapYearCountOffset getPreviousLeapYears .


Java , final, , , . , , , .

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(); }
}

0
1

x foo, , , .


JLS , , .

abstract class C {
  public final int x;

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

  abstract int f();
}

, x , , ,

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

, Java .

+2

final , . , ( ), !

+4

leapYearCountOffset , . getPreviousLeapYears , (0 null).

, - BeforeLeapYearEndPattern, , final, getPreviousLeapYears, .

0

, intrathread interthread.

, , : .

The same is true for fields final. Fields finalprovide additional guarantees for concurrent access, and these guarantees take effect only upon completion of the constructors. Therefore, it is not recommended to make fields finalavailable to other threads until the constructor completes. But it does not matter if you are not trying to access the field in question from other threads.

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

0
source

All Articles