2010-09-07 70 views
2

我在分配最终变量时遇到了一些奇怪的行为。你可以在构造函数中分配一个最终变量来初始化它,这是有道理的。但是你不能做同样在子类中,即使最终的变量是子类中的一员 -最终的奇怪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. 
     } 
    } 
} 

有人能想到一个很好的理由,为什么这应该/会以这种方式工作?

+0

没关系,在第二个构造函数中重新分配,没有看到它。 – 2010-09-07 20:18:27

回答

11

子类的每一个构造函数必须调用父类的构造函数它的第一次手术。每个最终的成员变量必须在构造器完成之前初始化。最终变量只能分配一次。根据这些规则,子类构造函数不可能直接为超类的成员分配一个值。

做出例外会增加复杂性并创造“陷阱”来换取有限的额外效用。

一个实际的解决方案是提供一个超类构造函数,该构造函数将一个值分配给最终成员。如果需要,这可以是protected或包私有。如果超类超出了你的控制范围,那么允许派生类打破其成员的最终状态的假设很可能会导致其他问题。

6

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

如果您的示例有效,那么这将意味着name可能具有不同的值(基于哪个类被实例化),使得修改器几乎是多余的。

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

+1

事实上,对于FinalTestSubclass的一个实例,该字段在不同时间会有不同的值:它将首先在超类构造函数中分配“FinalTest”,然后在子类构造函数中分配“FinalTestSubclass”。 – meriton 2010-09-07 20:26:39

+0

附录:非静态最终字段在不同对象中具有不同值是完全可以的。 – meriton 2010-09-07 20:34:48

3

非正式地,当构造函数完成时final字段应该已经初始化。

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

您可以改为想要这个:

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

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

这是Java规范的行为

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

对于你的情况,该变量在超类媒体链接分配,

你可以做的是

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

0

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

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