Использование конечных методов для инициализации переменной экземпляра
Из Sun docs
Обычно вы должны вводить код в инициализировать переменную экземпляра в конструктор.
Есть два альтернативы использованию конструктора для инициализировать переменные экземпляра: инициализировать блоки и конечные методы.
Я мог понять использование инициализированных блоков. Может ли кто-нибудь объяснить использование окончательных методов для инициализации var var? Неофициальный публичный сеттер может выполнять эту работу. Почему бы просто не использовать их?
Ответы
Ответ 1
Преимущество уже описано в том же учебнике Sun, с которым вы связались:
Окончательный метод не может быть переопределен в подклассе. Это обсуждается в уроке по интерфейсам и наследованию.
Это особенно полезно, если подклассы могут захотеть повторно использовать метод инициализации. Этот метод является окончательным, потому что вызов не конечных методов во время инициализации экземпляра может вызвать проблемы. Джошуа Блох описывает это более подробно в Эффективная Java (элемент 17 Дизайн и документ для наследования).
Причина, по которой не конечный метод является опасным при инициализации, заключается в том, что инициализация экземпляра суперкласса выполняется до того, как инициализируется подкласс. Поэтому, если нечетный метод переопределяется в подклассе и выполняется во время инициализации суперкласса, он может обращаться к неинициализированным полям подкласса, дающим ошибочные результаты.
Общее правило (цитирование из Effective Java): Конструкторы не должны ссылаться на переопределяемые методы, прямо или косвенно.
Ответ 2
Это объясняется на той же странице справочного руководства. Причина в том, что не конечный метод может быть переопределен по подклассу. Вот пример:
class Whatever {
private List<String> myVar = initializeInstanceVariable();
protected List<String> initializeInstanceVariable() {
return new ArrayList<String>();
}
}
class Whoever extends Whatever {
@Override
protected List<String> initializeInstanceVariable() {
return Collections.unmodifiableList(super.initializeInstanceVariable());
}
}
Итак, если вы создадите Whoever, myVar станет немодифицируемым; -)
Ответ 3
Другие примеры
Я сделал его компилируемым и упрощенным.
Duck.java
public class Duck {
String sound = "quack";
protected String speech;
public Duck() {
initSpeech();
}
protected void initSpeech() {
speech = "sound = " + sound;
}
public void speak() {
System.out.println(speech);
}
protected String getSound() {
return sound;
}
}
SqueakyDuck
public class SqueakyDuck extends Duck {
String squeakSound = "squeak";
public SqueakyDuck() {
super();
}
@Override
protected void initSpeech() {
speech = "sound = " + squeakSound;
}
@Override
protected String getSound() {
return squeakSound;
}
}
Main.java
public class Main {
public static void main(String[] args){
Duck squeaky = new SqueakyDuck();
squeaky.speak();
System.out.println(squeaky.getSound());
}
}
Вывод:
sound = null
squeak
Мой пример
Superclass.java
public class Superclass {
protected int x = m();
protected int m() {
return 8;
}
}
Subclass.java
public class Subclass extends Superclass {
private int y = 7;
@Override
protected int m() {
return y;
}
}
Main.java
public class Main {
public static void main(String[] args) {
Superclass s = new Subclass();
System.out.println(s.x);
}
}
Вывод:
0
Порядок выполнения:
-
main
-
m
from Subclass
(y
не инициализирован в этот момент, а 0
- значение по умолчанию для int
)
- конструктор
Superclass
- конструктор
Subclass