在Java中,为什么可以';我是否在父类中声明一个最终成员(不初始化它),并在子类中设置其值?我怎样才能工作?

在Java中,为什么可以';我是否在父类中声明一个最终成员(不初始化它),并在子类中设置其值?我怎样才能工作?,java,final,Java,Final,在Java程序中,我有多个子类继承自父类(抽象)。我想表达的是,每个孩子都应该有一个只设置一次的成员(我计划从构造函数中设置)。我的计划是编码s.th。像这样: public abstract class Parent { protected final String birthmark; } public class Child extends Parent { public Child(String s) { this.birthmark = s;

在Java程序中,我有多个子类继承自父类(抽象)。我想表达的是,每个孩子都应该有一个只设置一次的成员(我计划从构造函数中设置)。我的计划是编码s.th。像这样:

public abstract class Parent {
    protected final String birthmark;
}

public class Child extends Parent {
    public Child(String s) {
        this.birthmark = s;
    }
}
public abstract class Parent 
{
    protected final String birthmark;

    protected Parent(final String mark)
    {
        // only if this makes sense.
        if(mark == null)
        {
            throw new IllegalArgumentException("mark cannot be null");
        }

        birthmark = mark;
    }
}

public class Child 
    extends Parent 
{
    public Child(final String s) 
    {
        super(s);
    }
}
然而,这似乎并没有取悦爪哇诸神。在父类中,我得到消息
胎记
“可能尚未初始化”,在子类中,我得到“无法访问最终字段
胎记”


那么Java的方式是什么呢?我缺少什么?

将其传递给父构造函数:

public abstract class Parent {
    private final String birthmark;
    public Parent(String s) {
        birthmark = s;
    }
}

public class Child extends Parent {
    public Child(String s) {
        super(s);
    }
}
public abstract class Parent {
    protected final String birthmark;
    protected Parent(String s) {
        birthmark = s;
    }
}

public class Child extends Parent {
    public Child(String s) {
        super(s);
        ...
    }
}

您不能这样做,因为在比较父类时,编译器不能确定子类是否会初始化它。您必须在父级构造函数中初始化它,并让子级调用父级构造函数:

public abstract class Parent {
    private final String birthmark;
    public Parent(String s) {
        birthmark = s;
    }
}

public class Child extends Parent {
    public Child(String s) {
        super(s);
    }
}
public abstract class Parent {
    protected final String birthmark;
    protected Parent(String s) {
        birthmark = s;
    }
}

public class Child extends Parent {
    public Child(String s) {
        super(s);
        ...
    }
}

另一种Java风格的方法可能是让父类定义一个抽象的“getter”,并让子类实现它。在这种情况下,这不是一种很好的方法,但在某些情况下,它可以完全满足您的需要。

是的,最终成员将被分配到声明它们的类中。您需要向父类添加一个带字符串参数的构造函数。

在子类调用的超类中声明一个构造函数。
您必须在超类中设置字段以确保其已初始化,否则编译器无法确保字段已初始化。

您可能希望有一个父(字符串胎记)构造函数,以便确保在父类中始终初始化final。然后可以从Child()构造函数调用super(胎记)

为什么不将初始化委托给一个方法呢。然后重写父类中的方法

public class Parent {
   public final Object x = getValueOfX();
   public Object getValueOfX() {
      return y;
   }
}
public class Child {
  @Override
  public Object getValueOfX() {
     // whatever ...
  }
}

这应该允许自定义初始化。

我会这样做:

public abstract class Parent {
    protected final String birthmark;
}

public class Child extends Parent {
    public Child(String s) {
        this.birthmark = s;
    }
}
public abstract class Parent 
{
    protected final String birthmark;

    protected Parent(final String mark)
    {
        // only if this makes sense.
        if(mark == null)
        {
            throw new IllegalArgumentException("mark cannot be null");
        }

        birthmark = mark;
    }
}

public class Child 
    extends Parent 
{
    public Child(final String s) 
    {
        super(s);
    }
}
final意味着每个实例可以初始化变量一次。编译器无法确保每个子类都提供对birthmark的赋值,因此它会强制在父类的构造函数中进行赋值

public class Parent {
   public final Object x = getValueOfX();
   public Object getValueOfX() {
      return y;
   }
}
public class Child {
  @Override
  public Object getValueOfX() {
     // whatever ...
  }
}

我添加了检查null的功能,只是为了表明您还可以在一个位置而不是在每个构造函数中检查参数。

不是直接的答案,但在类文件格式中,最终实例字段只能由同一类文件设置,但不必在构造函数中设置。与IIRC相关的问题:如果我不声明孩子的构造函数(在这种情况下,可能会将家长的构造函数保持为公共的),我可以执行“new child(“set this”)”并得到我想要的,对吗?这个习惯用法真的是个坏主意。。。如果用户使getValueofX()依赖于子类的任何字段,则会出现NullPointerException,因为子类尚未实际构造。如果你对文档非常小心,它可以工作,但是愚蠢/恶意的用户可以在一瞬间破坏它。@Chamelaeon:这是一个如此愚蠢的评论,恶意或愚蠢的用户会为你的项目编写代码吗?恶意或愚蠢的用户可能会写入系统。退出(0)?这将有所帮助,谢谢!是否也有理由在函数final的声明中标记参数?编译器让我不用担心,我这样做是出于习惯。。。阻止您执行param=instanceVariable。如果hotspot能够更好地优化final vars(我认为现在不行),那么我的代码也会神奇地更快:-)在构造函数中调用抽象getter方法可能会有潜在的危险,但是,一个好规则是永远不要直接或间接地调用可重写的方法,从一个构造器中。@tobeer任何例子有多危险?这个答案对我来说非常有效!编辑如果调用构造函数中的重写方法,并且该方法访问子类中的变量,则该变量尚未初始化,因为子类的构造函数尚未实例化。这是危险的,因为它现在可能工作,但如果有人以后更改getter,它将崩溃或获取错误的值。在我的情况下,我无法传递,因为对象需要引用“this”,有提示吗?