вызов сеттера из конструктора - PullRequest
58 голосов
/ 04 февраля 2011

Каковы "за" и "против" вызова мутатора из конструктора (если есть)

, то есть:

public MyConstructor(int x) {
  this.x = x;
}

против:

public MyConstructor(int x) {
  setX(x);
}

public void setX(int x) {
  this.x = x;
}

У вас есть предпочтения?(Это не домашняя работа, просто посмотрите на наш документ по стандартам кодирования, где говорится, что всегда нужно обращаться к мутаторам при установке экземпляров var в конструкторе, и я не всегда так поступаю)

Ответы [ 9 ]

76 голосов
/ 04 февраля 2011

Лично я бы установил переменную непосредственно в большинстве случаев.

Методы обычно ожидают, что экземпляр полностью сформирован к тому времени, когда они вызваны,В частности, вызов переопределенного метода из конструктора - это рецепт сложного для понимания кода и трудно обнаруживаемых ошибок.

Сказав это, я часто стараюсь сделать классы неизменяемыми в любом случае, и в этом случаене только нет никакого установщика, но у вас есть , чтобы в любом случае установить конечную переменную из конструктора (или инициализатора переменной):)

Там, где свойства имеют логику, логика сеттера обычно проверяется ииногда меняют распространение на наблюдателей.Обычно я ожидаю, что параметры конструктора будут проверены явно в начале метода, и вы не захотите хотеть распространения каких-либо изменений до того, как экземпляр будет полностью создан.

27 голосов
/ 04 февраля 2011

Я соблюдаю два правила в отношении конструкторов, чтобы минимизировать проблемы, поэтому я не буду использовать метод мутатора:

Конструкторы (не финальных классов) должны вызывать только финальные или частные методы.Если вы решили игнорировать это правило и позволить конструктору вызывать не окончательные / не приватные методы, то:

  • эти методы и любые методы, которые они могут вызывать, должны быть осторожны, чтобы не допустить, что экземпляр полностьюинициализируется, и
  • подклассы, которые переопределяют эти методы (подклассы, которые могут даже не знать, что конструктор суперкласса вызывает эти методы), не должны предполагать, что конструктор подкласса и конструкторы суперкласса были полностью выполнены.Эта проблема усугубляется по мере того, как глубже в иерархии наследования используется суперкласс с «злым» конструктором.

Стоит ли весь этот дополнительный познавательный багаж? Можно допустить исключение дляпростые мутаторы, которые присваивают значение только переменной экземпляра, поскольку это дает мало пользы, даже если кажется, что оно того не стоит.

[[@Jon Skeet упоминает об этом в своем ответе: «... В частности,вызов переопределенного метода из конструктора - это рецепт сложного для понимания кода и трудно обнаруживаемых ошибок ».Но я не думаю, что последствия этой проблемы достаточно подчеркнуты.]]

Конструкторы должны быть осторожны с утечкой this до полной инициализации экземпляра. В то время как предыдущее правило касалось методов внутри класса и подклассов, обращающихся к ivarsВы также должны быть осторожны с (даже окончательными / закрытыми) методами, передающими this другим классам и служебным функциям до полной инициализации this.Чем больше не закрытых, переопределяемых методов, которые вызывает конструктор, тем выше риск утечки this.


Некоторые ссылки на конструкторы, вызывающие не финальные, не приватные методы:

https://www.securecoding.cert.org/confluence/display/java/MET05-J.+Ensure+that+constructors+do+not+call+overridable+methods

http://www.javaworld.com/article/2074669/core-java/java-netbeans--overridable-method-call-in-constructor.html

http://www.javaspecialists.eu/archive/Issue210.html

2 голосов
/ 04 февраля 2011

Я редко делаю это, и у меня никогда не было проблем из-за этого.Это также может иметь непредвиденные последствия, особенно если ваши сеттеры переопределены в подклассе или если ваши сеттеры запускают дополнительные вызовы методов, которые могут не подходить при инициализации.

1 голос
/ 24 декабря 2015

Вызов любых public, static, non-final методов в конструкторе - решать вам, но наилучшая практика - никогда не вызывать такие методы внутри конструктора, потому что эти методы могут быть переопределены в подклассах, и фактически будет вызываться только переопределенная версия этих методов (если вы используете полиморфное поведение).

Например:

public class Foo {

public Foo() {
    doSmth(); // If you use polymorphic behavior this method will never be invoked
}

public void doSmth() {
    System.out.println("doSmth in super class");
}

public static void main(String[] args) {
    new Bar(200);
}
}

class Bar extends Foo {

private int y;;

public Bar(int y) {
    this.y = y;
}

@Override
public void doSmth() { // This version will be invoked even before Barr object initialized
    System.out.println(y);
}

}

Будет напечатано 0.

Подробнее см. Брюс Экель "Мышление на Яве" глава "Полиморфизм"

1 голос
/ 04 февраля 2011

Существует одна причина, по которой наши стандарты кодирования требуют использования методов доступа (которые также могут быть частными).Если вы хотите быстро выяснить, какой код меняет поле, вы просто вызываете иерархию вызовов для установщика в Eclipse!

Это значительно экономит время в кодовой базе, которая достигла 2 миллионов строк кода, а также упрощает рефакторинг.

1 голос
/ 04 февраля 2011

Я предпочитаю устанавливать их непосредственно в конструкторе по нескольким причинам.Во-первых, что-то вроде this.x = x; так же ясно, если не больше, чем вызов отдельного метода, который делает то же самое.Во-вторых, метод мог быть потенциально переопределен, если он не помечен как финальный, и вызов потенциально переопределенных методов из конструктора - это большая проблема в моей книге.В-третьих, большинство методов обычно предполагают, что объект уже завершен, когда они выполняются, а не на полпути к созданию.Хотя это не должно вызывать никаких проблем в этом простом случае, в более сложных случаях это может привести к серьезным незначительным ошибкам, требующим отслеживания возрастов.

Основной аргумент в пользу использования сеттеров / геттеров везде - этоможно переименовать поле, просто изменив его имя в 3 местах, его определение, методы получения / установки, и все должно скомпилироваться и быть в порядке.На мой взгляд, этот аргумент в настоящее время является недействительным, поскольку любая приличная современная среда IDE переименовывает все вхождения такого поля простым сочетанием клавиш.

1 голос
/ 04 февраля 2011

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

1 голос
/ 04 февраля 2011

Зависит от того, как вы относитесь к сеттерам.Если вы считаете, что класс может быть производным, вы можете разрешить такой вызов, чтобы вы могли переопределить поведение в другом месте.В противном случае вы можете установить его как есть.

Разумеется, явный вызов метода Setter также обеспечит способы внедрения дополнительных параметров поведения во время настройки (например, применение защиты в случае необходимости).

0 голосов
/ 04 февраля 2011

Я тоже не против, и у меня нет сильных предпочтений [и не придерживаюсь некоторых строгих стандартов], просто используйте свое суждение. Но если вы используете сеттеры, любой, кто переопределяет класс, должен знать, что объект может быть создан не полностью. При этом убедитесь, что никакой дополнительный код в установщиках не может опубликовать ссылку this.

Сеттеры могут быть полезны для стандартных проверок диапазона и т. Д., Поэтому дополнительный код для проверки ввода не требуется. Повторное использование сеттеров - более грязный подход.

Использование поля имеет очевидные преимущества (например, уверенность в том, что любой подкласс не сможет злонамеренно изменить поведение), и это обычно предпочитается, особенно. в библиотеках.

На самом деле c-tor - это один из 3 возможных способов создания объекта (сериализация + клон - 2 других), возможно, необходимо обработать некоторое переходное состояние вне c-tor, так что у вас все еще есть, что задуматься о полях. против сеттеров. (Правка, есть еще одна половина пути: небезопасно, но это то, что использует сериализация)

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