Почему вам разрешено вызывать один конструктор из другого?

Я смотрел на другие вопросы о SO, но я действительно не видел объяснений по моему вопросу. Я прочитал, что вызов конструктора из другого конструктора (с использованием этого ключевого слова) был действительным, но я не понял, почему он был прав.

Раньше я думал, что на объект может действовать только один конструктор. Цепочка конструктора, похоже, нарушает эту логику, в то время как при вызове одного конструктора он запускает другой вместе с исходным, целевым конструктором. Почему работает цепочка конструкторов?

Ответы

Ответ 1

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

Представьте, что вы создаете шину.

public class Bus {  
      int noOfSeats;  
      String busColor;  

      public Bus() {  
           this(40); //// Using another constructor and proceeding with default values..   
      }  
      public Bus(int seats) {   
           this(seats,"red"); // Using another constructor and proceeding..   
      }  
      public Bus(int seats, String color) {  
           this.noOfSeats = seats; 
           this.busColor = color;  
      }  
 } 

И человек, использующий этот класс, может использовать только конструктор за раз, когда вы используете внутреннюю привязку. Представьте, что у вас есть метод, который инициализирует вещи со значениями по умолчанию и вызывает их в конструкторе. Нет ничего плохого в этом, верно?

Чтобы уточнить, человек, создающий объект, вызывает только один конструктор. Вызываемый конструктор вызывает другие, которые являются внутренними для этого класса.

Ответ 2

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

Пока эта функция не была разработана на языках, нам приходилось полагаться на функции "инициализации", которые вызывались из конструкторов. Это было больно по двум причинам (i) вы никогда не могли гарантировать, что функции вызываются только от конструкторов, и (ii) вызов методов класса из конструкторов всегда является "запахом" дизайна.

Ответ 3

Это позволяет прояснить логику и уменьшить код.

Являются ли конструкторы вложенности (или factory) хорошими, или каждый из них выполняет все init-работы

Разумно объединить конструкторы вместе, версия с меньшим количеством параметров вызывает версию с большим количеством параметров. Он очень четко показывает, что происходит, и вся настоящая "логика" (за пределами значений по умолчанию) находится в одном месте. Например:

public class Foo {

    private static final int DEFAULT_X =10;
    private static final int DEFAULT_Y =20;

    private int x;
    private int y;
    private int precomputedValue;

    public Foo(int x, int y) {
        this.x = x;
        this.y = y;
        precomputedValue = x * y;
    }

    public Foo(int x) {
        this(x, DEFAULT_Y);
    }

    public Foo() {
        this(DEFAULT_X, DEFAULT_Y)
    }
}

Ответ 4

Не Java dev здесь, но в Python это также разрешено и в значительной степени нормальным.

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

Это позволяет вам использовать функции построения родительского класса. Всякий раз, когда вы не знаете, что именно делает родительский класс, вы обычно вызываете его сначала, чтобы не нарушать функции и методы родительского класса.

Ответ 5

Тот факт, что путаница возникает из каждого экземпляра, должен назвать один ctor абсолютно прав. Конструктор, закованный в цепочку другой, используется для избежания дублирования кода и простоты, поскольку копирование одного и того же кода действительно накладных расходов. Иногда, возможно, нам нужны два конструктора, сначала один принимает два параметра, другой принимает три параметра. Но три параметра-ctor используют одни и те же переменные двух параметров-ctor. Вы предпочитаете копировать-вставить то же самое назначение или просто называть другой ctor this?

Ответ 6

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

Еще одна стратегия построения конструктора, которую я нахожу полезной, - это реализация по умолчанию/автоматическая инжекция для интерфейсов.

public class Foo {

    private readonly ILogger _logger;

    //Default implementation for most cases
    public Foo() : this(new DefaultLoggerImplementation()) {}

    //DI Constructor that allows for different implementations of ILogger 
   //(Or Mocks in testing)
    public Foo(ILogger logger)
    {
        _logger = logger;
    }

    public void Log(string message)
    {
        _logger.log(message);
    } 
}

Таким образом, если у вас есть реализация по умолчанию для интерфейса, она автоматически вводится через new Foo()

Если у вас есть случай с отличным поведением ILogger, это легко впрыскивается, это также способствует возможности Mock интерфейса в вашей модульной тестовой среде.