我在最终变量的分配中遇到了一些奇怪的行为。您可以在构造函数中指定最终变量来初始化它,这是有道理的。但是,即使最终变量是子类的成员 -
,也不能在子类中执行相同的操作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.
}
}
}
有人可以想出为什么这应该/会以这种方式运作的好理由吗?
答案 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()
{
}
}
}
答案 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");
}
}
}