首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在构造函数退出之前访问最后一个变量

在构造函数退出之前访问最后一个变量
EN

Stack Overflow用户
提问于 2012-04-12 18:21:48
回答 4查看 214关注 0票数 3

好的,我一直在处理优秀的JodaTime库,试图实现一个通用的零售/财政日历(4-5-4)。我已经找到了我的公司的具体案例,但一般情况(主要是确定年初和闰年)是致命的;例如,有一套日期,两个财政年度(通常364天,通常)将从一个ISO年开始。

在确定年度开始规则的过程中,我最终得到了一个抽象类和几个用于确定年度开始的具体类,它们是基于ISO闰日的哪一边下降的。

(缩减)抽象类:

代码语言:javascript
复制
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);
    }
}

混凝土类(日期在1/7 -2/28范围内):

代码语言:javascript
复制
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的状态是什么?它是否以任何方式得到了保证,或者说,它是否可以随心所欲地改变?我怎么能测试这个才能知道呢?我已经意识到编译器可以自由地重新排列一些语句,但是会(可以吗?)在这里发生的?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-04-12 18:26:22

由于getPreviousLeapYears是在分配leapYearCountOffset之后调用的,因此leapYearCountOffset将被正确初始化,getPreviousLeapYears将看到正确的值。

Java留下了确保在第一次访问之前正确初始化在构造函数期间调用的代码访问的final变量的负担。如果未正确初始化,在构造函数期间调用的代码将看到该字段类型的零值。

程序

代码语言:javascript
复制
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(); }
}

版画

代码语言:javascript
复制
0
1

因为x在第一次调用foo时没有初始化,但是正如上面所解释的,您没有这个问题。

JLS说,构造函数中每次使用final都必须在初始化之后,但对于其他方法却没有这样的保证。考虑一下

代码语言:javascript
复制
abstract class C {
  public final int x;

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

  abstract int f();
}

为了确保在每次使用之前初始化x,它需要确保不存在类似于

代码语言:javascript
复制
class D extends C {
  int f() {
    return this.x;
  }
}

这就需要对类进行全局推理,这与Java的动态链接不一致,并给语言规范增加了许多复杂性。

票数 2
EN

Stack Overflow用户

发布于 2012-04-12 18:26:11

final变量的一个保证是编译器在分配它们之前不会让您访问它们。所以,如果它编译(它应该编译),那么您就可以继续了!

票数 4
EN

Stack Overflow用户

发布于 2012-04-12 18:37:54

虽然leapYearCountOffset保证有其最终价值,但这仍然是一个等待发生的意外。方法getPreviousLeapYears是在子类初始化启动之前执行的,因此子类中的任何变量都有其默认值(0或null)。

现在没有危险,但是如果有人进来并更改BeforeLeapYearEndPattern,也许通过添加一个新的final实例变量(然后在getPreviousLeapYears中使用),您会受到伤害。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10129468

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档