最终的奇怪的java分配规则

时间:2010-09-07 20:09:59

标签: java

我在最终变量的分配中遇到了一些奇怪的行为。您可以在构造函数中指定最终变量来初始化它,这是有道理的。但是,即使最终变量是子类的成员 -

,也不能在子类中执行相同的操作
public class FinalTest {
    public final String name;

    public FinalTest()
    {
        name = "FinalTest";
    }

    public static class FinalTestSubclass extends FinalTest {
        public FinalTestSubclass()
        {
            name = "FinalTestSubclass"; //<---- this won't compile, assignment to final variable.
        }
    }
}

有人可以想出为什么这应该/会以这种方式运作的好理由吗?

5 个答案:

答案 0 :(得分:11)

子类的每个构造函数都必须调用超类的构造函数作为其第一个操作。必须在构造函数完成之前初始化每个最终成员变量。最终变量只能分配一次。根据这些规则,子类构造函数不可能直接将值赋给final超类'成员。

制造例外将增加复杂性并创造“陷阱”以换取有限的额外效用。

一个实际的解决方案是提供一个超类构造函数,它将一个值赋给最终成员。如果需要,可以是protected或包私有。如果超类不受你的控制,那么允许派生类打破其成员终结性的假设很有可能会导致其他问题。

答案 1 :(得分:6)

如果您被允许在name中为FinalTestSubClass分配值,则意味着FinalTest中指定的值实际上不是最终值。

如果您的示例有效,则这意味着name可能具有不同的值(基于实例化的类),使final修饰符非常冗余。

一个更好的问题是,为什么 应该允许你想要的行为?

答案 2 :(得分:3)

非正式地,最终字段应该在构造函数完成时初始化。

在你的子类构造函数中,super()被隐式调用,超类的构造函数已经完成,超类中的最后一个字段不应该被修改。

你可能想要这个:

class A
    final String s;
    A(String s){ this.s = s; }
    A() { this("default"); }

class B extends A
    B(){ super("B's default"); }

答案 3 :(得分:1)

这是Java中的标准行为

关键词final可以通过多种方式使用,对于类关闭从中继承的可能性,对于覆盖它的方法,对于变量只允许在简单的单词中分配一次。

对于您的情况,此变量已在超类中分配,

你可以做的是

public class FinalTest {
    public final String  name = "FinalTest";

    public FinalTest()
    {

    }

    public static class FinalTestSubclass extends FinalTest {

        public final String  name = "FinalTestSubclass";

        public FinalTestSubclass()
        {

        }
    }
}

Read more about final variables

答案 4 :(得分:0)

回复你对matt的回答的评论;你可以通过在构造函数中传递它来确定子类中的常量:

public class FinalTest {
    public final String name;

    public FinalTest()
    {
        this("FinalTest");
    }

    protected FinalTest(String nameConstant)
    {
        name = nameConstant;
    }

    public static class FinalTestSubclass extends FinalTest {
        public FinalTestSubclass()
        {
            super("FinalTestSubclass");
        }
    }
}