В Java почему я не могу объявить конечный член (без его инициализации) в родительском классе и установить его значение в подклассе? Как я могу работать?
В программе Java у меня есть несколько подклассов, наследующих от родителя (который является абстрактным). Я хотел бы выразить, что у каждого ребенка должен быть член, который установлен только один раз (который я планировал сделать из конструктора). Мой план состоял в том, чтобы кодировать s.th. например:
public abstract class Parent {
protected final String birthmark;
}
public class Child extends Parent {
public Child(String s) {
this.birthmark = s;
}
}
Однако это, похоже, не радует богов Java. В родительском классе я получаю сообщение о том, что birthmark
"возможно, не было инициализировано", в дочернем классе я получаю "Конечное поле birthmark
невозможно получить".
Итак, что такое Java-способ для этого? Что мне не хватает?
Ответы
Ответ 1
Вы не можете этого сделать, потому что, сравнивая родительский класс, компилятор не может быть уверен, что подкласс инициализирует его. Вы должны инициализировать его в родительском конструкторе и иметь дочерний вызов родительского конструктора:
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);
...
}
}
Ответ 2
Передайте его родительскому конструктору:
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);
}
}
Ответ 3
Другой способ Java-ish сделать это, вероятно, состоит в том, чтобы родительский класс определял абстрактный "getter", и чтобы дети его реализовали. Это не отличный способ сделать это в этом случае, но в некоторых случаях это может быть именно то, что вы хотите.
Ответ 4
Я бы сделал это следующим образом:
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.
Ответ 5
Почему бы не делегировать инициализацию методу. Затем переопределите метод в родительском классе.
public class Parent {
public final Object x = getValueOfX();
public Object getValueOfX() {
return y;
}
}
public class Child {
@Override
public Object getValueOfX() {
// whatever ...
}
}
Это должно позволить пользовательскую инициализацию.
Ответ 6
Да, конечные члены должны быть назначены в классе, в котором они объявлены. Вам нужно добавить конструктор с аргументом String в Parent.
Ответ 7
Объявить конструктор в суперклассе, вызываемом подклассом.
Вы должны установить поле в суперклассе, чтобы убедиться, что оно инициализировано, или компилятор не может убедиться, что поле инициализировано.
Ответ 8
Вероятно, вы захотите создать конструктор родительского типа (String birthmark), чтобы вы могли обеспечить в своем классе Parent окончательное инициализацию. Затем вы можете вызвать супер (родимое пятно) из вашего конструктора Child().