Перегрузка конструктора в Java - лучшая практика

Есть несколько тем, похожих на эту, но я не смог найти одну с достаточным ответом.

Я хотел бы знать, что является лучшей практикой для перегрузки конструктора в Java. У меня уже есть свои мысли по этому вопросу, но я хотел бы услышать больше советов.

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

Спасибо:)

5 ответов

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

public class Simple {

    public Simple() {
        this(null);
    }

    public Simple(Resource r) {
        this(r, null);
    }

    public Simple(Resource r1, Resource r2) {
        // Guard statements, initialize resources or throw exceptions if
        // the resources are wrong
        if (r1 == null) {
            r1 = new Resource();
        }
        if (r2 == null) {
            r2 = new Resource();
        }

        // do whatever with resources
    }

}

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

Сделать класс параметров

public class SimpleParams {
    Resource r1;
    Resource r2;
    // Imagine there are setters and getters here but I'm too lazy 
    // to write it out. you can make it the parameter class 
    // "immutable" if you don't have setters and only set the 
    // resources through the SimpleParams constructor
}

Конструктор в Simple только должен разделить SimpleParams параметр:

public Simple(SimpleParams params) {
    this(params.getR1(), params.getR2());
}

... или сделать SimpleParams атрибут:

public Simple(Resource r1, Resource r2) {
    this(new SimpleParams(r1, r2));
}

public Simple(SimpleParams params) {
    this.params = params;
}

Сделать заводской класс

Создайте фабричный класс, который инициализирует ресурсы для вас, что удобно, если инициализация ресурсов немного сложна:

public interface ResourceFactory {
    public Resource createR1();
    public Resource createR2();
}

Затем конструктор выполняется так же, как и в классе параметров:

public Simple(ResourceFactory factory) {
    this(factory.createR1(), factory.createR2());
} 

Сделайте комбинацию обоих

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

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

Одним из примеров этого может быть JTable - основной конструктор принимает TableModel (плюс столбец и модели выбора) и другие конструкторы вызывают этот первичный конструктор.

Для подклассов, где у суперкласса уже есть перегруженные конструкторы, я бы предположил, что разумно рассматривать любой из конструкторов родительского класса как первичный, и думаю, что вполне законно не иметь единственного первичного конструктора. Например, при расширении ExceptionЯ часто предоставляю 3 конструктора, один из которых занимает всего String сообщение, один принимая Throwable причина, а другой принимает оба. Каждый из этих конструкторов вызывает super непосредственно.

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

Builder - это вложенный класс с методами, предназначенными только для установки полей, и тогда конструктор ComplexClass принимает в качестве аргумента только такой Builder.


Редактировать: конструктор ComplexClass может гарантировать, что состояние в Builder является действительным. Это очень сложно сделать, если вы просто используете сеттеры на ComplexClass.

Это действительно зависит от вида классов, поскольку не все классы созданы равными.

В качестве общего руководства я бы предложил 2 варианта:

  • Для классов value & immutable (Exception, Integer, DTO и т. Д.) Используйте один первичный конструктор, как предложено в ответе выше.
  • Для всего остального (сессионные компоненты, сервисы, изменяемые объекты, сущности JPA & JAXB и т. Д.) Используйте конструктор по умолчанию только с разумными значениями по умолчанию для всех свойств, поэтому его можно использовать без дополнительной настройки.

Ну, вот пример для перегруженных конструкторов.

public class Employee
{
   private String name;
   private int age;

   public Employee()
   {
      System.out.println("We are inside Employee() constructor");
   }

   public Employee(String name)
   {
      System.out.println("We are inside Employee(String name) constructor");
      this.name = name;
   }

   public Employee(String name, int age)
   {
      System.out.println("We are inside Employee(String name, int age) constructor");
      this.name = name;
      this.age = age;
   }

   public Employee(int age)
   {
      System.out.println("We are inside Employee(int age) constructor");
      this.age = age; 
   }

   public String getName()
   {
      return name;
   }

   public void setName(String name)
   {
      this.name = name;
   }

   public int getAge()
   {
      return age;
   }

   public void setAge(int age)
   {
      this.age = age;
   }
}

В приведенном выше примере вы можете увидеть перегруженные конструкторы. Имя конструкторов одинаковое, но каждый из них имеет разные параметры.

Вот некоторые ресурсы, которые проливают больше света на перегрузку конструктора в Java,

Конструкторы

Объяснение конструктора.

Перегрузка конструктора похожа на перегрузку метода. Конструкторы можно перегружать для создания объектов разными способами.

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

Для получения дополнительных сведений о конструкторе java посетите https://tecloger.com/constructor-in-java/

Другие вопросы по тегам