12

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

public abstract class Parent {
    protected final String birthmark;
}

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

然而,这似乎并不能取悦 Java 之神。在父类中,我收到birthmark“可能尚未初始化”的消息,在子类中,我收到“birthmark无法访问最终字段”。

那么,Java 的方式是什么?我错过了什么?

4

8 回答 8

20

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

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);
        ...
    }
}
于 2009-03-19T18:48:42.923 回答
7

将其传递给父构造函数:

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);
    }
}
于 2009-03-19T18:48:31.647 回答
2

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

于 2009-03-19T18:49:24.420 回答
2

我会这样做:

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 意味着变量可以在每个实例中初始化一次。编译器无法确保每个子类都会提供对胎记的赋值,因此它会强制赋值发生在父类的构造函数中。

我添加了对 null 的检查只是为了表明您还可以获得能够在一个地方而不是每个 cosntructor 中检查参数的好处。

于 2009-03-19T18:53:15.550 回答
1

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

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

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

于 2009-03-19T18:50:19.940 回答
0

是的,最终成员将在声明它们的类中分配。您需要向 Parent 添加一个带有 String 参数的构造函数。

于 2009-03-19T18:49:28.553 回答
0

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

于 2009-03-19T18:49:56.930 回答
0

您可能希望有一个 Parent(Stringbirthmark) 构造函数,以便您可以确保在您的 Parent 类中始终初始化 final。然后你可以从你的 Child() 构造函数中调用 super(birthmark) 。

于 2009-03-19T18:50:01.377 回答