Использование финальных методов для инициализации переменной экземпляра - PullRequest
14 голосов
/ 28 января 2010

Из ВС документов

Обычно вы должны поместить код в инициализировать переменную экземпляра в Конструктор.
Есть два альтернативы использованию конструктора инициализировать переменные экземпляра: инициализировать блоки и финальные методы.

Я мог понять, как использовать блоки инициализации. Может кто-нибудь объяснить, пожалуйста, использование окончательных методов для инициализации экземпляра var? Не финальный публичный сеттер может сделать эту работу. Почему бы просто не использовать их?

Ответы [ 3 ]

21 голосов
/ 28 января 2010

Преимущество уже описано в том же учебнике Sun, с которым вы связаны:

Последний метод не может быть переопределен в подклассе. Это обсуждается в уроке об интерфейсах и наследовании.

Это особенно полезно, если подклассы могут захотеть повторно использовать метод инициализации. Метод является окончательным, потому что вызов не финальных методов во время инициализации экземпляра может вызвать проблемы. Джошуа Блох описывает это более подробно в Effective Java (пункт 17 «Дизайн и документ для наследования») .

Причина, по которой не финальный метод опасен при инициализации, заключается в том, что инициализация экземпляра суперкласса выполняется до инициализации подкласса. Поэтому, если неконечный метод переопределяется в подклассе и выполняется во время инициализации суперкласса, он может обращаться к неинициализированным полям подкласса, давая ошибочные результаты.

Общее правило (цитата из Effective Java): Конструкторы не должны вызывать переопределяемые методы, прямо или косвенно.

3 голосов
/ 01 февраля 2016

Другие примеры

С alykhantejani.github.io

Я сделал его компилируемым и упростил.

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 из Subclass (y это не инициализировано в данный момент и 0 является значением по умолчанию для int)
  • конструктор Superclass
  • конструктор Subclass
3 голосов
/ 28 января 2010

Это объясняется на той же странице учебника, на который есть ссылки. Причина в том, что неконечный метод может быть переопределен восходящим подклассом. Вот пример:

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 станет неизменным; -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...