Как в Java вызвать один конструктор из другого?

avatar
ashokgelal
12 ноября 2008 в 20:10
970872
23
2444

Можно ли вызвать конструктор из другого (в том же классе, а не из подкласса)? Если да, то как? И как лучше всего вызвать другой конструктор (если есть несколько способов сделать это)?

Источник
David Medinets
6 июля 2018 в 19:49
2

Я считаю, что посылка вашего вопроса неверна. Вместо вызова конструктора внутри конструктора используйте шаблон Factory. Статический фабричный метод сначала создает все объекты нижнего уровня. Затем он создает объекты более высокого уровня, которые получают результаты от вызова фабрики. Этот метод устраняет сложность модели, что способствует поддержанию, ясности и тестированию.

Richard
21 января 2019 в 12:27
1

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

Augusto
24 июля 2019 в 19:16
0

Срой, но это не очень хорошая практика, если вы хотите сделать что-то подобное, перезарядите конструктор. Если вы хотите обернуть контент, это можно сделать, но для другого файла pourpuse. Не конструктор открытого класса Foo {private int x; public Foo () {} public Foo (int x) {this.x = x; } public Foo (int x, int y) {this.x = x; this.y = y}

Stuporman
18 марта 2021 в 16:36
0

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

Ответы (23)

avatar
Jon Skeet
12 ноября 2008 в 20:12
3182

Да, это возможно:

public class Foo {
    private int x;

    public Foo() {
        this(1);
    }

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

Для привязки к конкретному конструктору суперкласса вместо конструктора в том же классе используйте super вместо this. Обратите внимание, что вы можете связать только один конструктор , и он должен быть первым оператором в теле вашего конструктора .

См. Также этот связанный вопрос, который касается C #, но где применяются те же принципы.

gsingh2011
2 ноября 2012 в 18:02
37

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

Jon Skeet
2 ноября 2012 в 18:06
34

@ gsingh2011: Верно. Вы можете связать только один другой конструктор.

Christian Fries
11 марта 2013 в 20:34
48

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

Ali
13 мая 2013 в 07:23
10

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

Trikaldarshiii
16 июля 2013 в 21:38
0

@JonSkeet Да! мы можем связать с одним другим конструктором, но на самом деле у нас может быть очень длинная цепочка

Jon Skeet
16 июля 2013 в 21:46
0

@Mohit: И почему это проблема? На самом деле непонятно, каков контекст вашего комментария ...

Trikaldarshiii
16 июля 2013 в 22:13
0

нет, не проблема, я только что добавил к вашему комментарию, что "только еще один конструктор", поэтому многие другие создают длинную цепочку

Harshit Gupta
27 июня 2015 в 11:18
0

В обязательном порядке, если вы используете this () или super (), они должны быть первым оператором конструктора, и оба они не могут использоваться в конструкторе.

Francis
28 июня 2015 в 16:01
0

@ gsingh2011 да, по какой-то причине разрешен только один конструктор класса.

Jon Skeet
28 июня 2015 в 16:32
0

@Francis: Я предполагаю, что вы имеете в виду «вы можете связать только один другой конструктор» - в настоящее время ваше утверждение звучит так, как будто вы можете иметь только один конструктор на класс, что явно неверно.

Francis
29 июня 2015 в 11:06
0

@JonSkeet да, я имел в виду цепочку только с одним другим конструктором. спасибо за исправление, и я прошу прощения за мое вводящее в заблуждение заявление.

Justin Time - Reinstate Monica
9 февраля 2016 в 18:50
0

@Francis Я не уверен, но я считаю, что причина в том, что если первая строка конструктора делегирует другому конструктору или вызывает конструктор суперкласса, он фактически запускает до , когда объект создается. C ++ имеет такое же ограничение, у вас может быть только один вызов конструктора родительского класса или (как в C ++ 11) одно делегирование для каждого конструктора, и не более того, при этом оба выполняются до создания объекта.

Jon Skeet
9 февраля 2016 в 18:52
0

@JustinTime: Вам нужно определить, что вы имеете в виду под «до того, как объект будет построен», чтобы я мог судить об этом ...

Justin Time - Reinstate Monica
9 февраля 2016 в 18:59
0

@JonSkeet Я имею в виду, что исходя из того, что я знаю о Java, вызовы конструктора делегирования или суперкласса выполняются до инициализации полей объекта и переводятся в состояние, в котором он считается «созданным». В частности, я считаю, что процесс: 1) Запуск конструктора родительского класса / суперкласса для инициализации унаследованной части класса, 2) Инициализация переменных-членов, зависящих от экземпляра, чтобы объект был действительным, чтобы доступ к члену в теле конструктора ничего не сломал. , 3) Выполнить тело конструктора, чтобы завершить построение. Он считается построенным к концу шага 3.

Justin Time - Reinstate Monica
9 февраля 2016 в 19:01
0

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

Justin Time - Reinstate Monica
9 февраля 2016 в 19:15
0

Создавая объект, я имею в виду выделение памяти, установку типа и инициализацию полей, чтобы объект находился в допустимом, пригодном для использования состоянии (неявный первый шаг каждого конструктора), с делегированием, передающим задачу сделать объект действительным и можно использовать в конструкторе, которому делегирован. Не знал, что объекты не запускаются как их фактический тип в C ++, но имеет смысл рассмотреть, как C ++ неявно связывает конструктор (ы) родительского класса для создания унаследованной части производного класса перед фактическим выполнением конструктора производного класса; Думаю, мне придется этого опасаться.

Andrej
26 сентября 2019 в 19:07
1

Я знаю, что опаздываю в раздел комментариев, но не понимаю, почему new Foo(1); не работает?

Jon Skeet
26 сентября 2019 в 22:10
2

@Andrej: Это действительно было бы «вызовом одного конструктора из другого», но он не будет делать то, что хочет OP, а именно инициализировать один объект с помощью нескольких конструкторов, один соединяется с другим. Создание двух объектов путем простого создания отдельного объекта в одном вызове конструктора - это совсем не одно и то же.

mrhotroad
21 мая 2020 в 14:23
0

Что за ублюдок это придумал? Тысячи людей задаются вопросом.

Jon Skeet
21 мая 2020 в 14:28
0

@mrhotroad: Пожалуйста, пишите на этом сайте по-английски.

avatar
keshari abeysinghe
7 июля 2021 в 18:04
0

Это называется цепочкой конструкторов. Цепочка конструкторов - это процесс вызова одного конструктора из другого конструктора по отношению к текущему объекту. Цепочку конструктора можно выполнить двумя способами:

1. В том же классе: это можно сделать с помощью ключевого слова this () для конструкторов в том же классе. 2. Из базового класса: с помощью ключевого слова super () для вызова конструктора из базового класса.

avatar
Anil Nivargi
16 января 2021 в 02:12
0

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

Пример: -

 public class Example {
   
      private String name;
   
      public Example() {
          this("Mahesh");
      }

      public Example(String name) {
          this.name = name;
      }

 }
avatar
Josh
26 августа 2019 в 09:35
270

Использование this(args). Предпочтительный шаблон - от наименьшего конструктора к наибольшему.

public class Cons {

    public Cons() {
        // A no arguments constructor that sends default values to the largest
        this(madeUpArg1Value,madeUpArg2Value,madeUpArg3Value);
    }

    public Cons(int arg1, int arg2) {
       // An example of a partial constructor that uses the passed in arguments
        // and sends a hidden default value to the largest
        this(arg1,arg2, madeUpArg3Value);
    }

    // Largest constructor that does the work
    public Cons(int arg1, int arg2, int arg3) {
        this.arg1 = arg1;
        this.arg2 = arg2;
        this.arg3 = arg3;
    }
}

Вы также можете использовать недавно отстаиваемый подход valueOf или просто «из»:

public class Cons {
    public static Cons newCons(int arg1,...) {
        // This function is commonly called valueOf, like Integer.valueOf(..)
        // More recently called "of", like EnumSet.of(..)
        Cons c = new Cons(...);
        c.setArg1(....);
        return c;
    }
} 

Чтобы вызвать суперкласс, используйте super(someValue). Вызов super должен быть первым вызовом в конструкторе, иначе вы получите ошибку компилятора.

koppor
13 ноября 2012 в 20:16
28

Если используется много параметров конструктора, подумайте о строителе. См. Пункт 2 книги Джошуа Блоха «Эффективная Java».

YoYo
30 января 2016 в 06:46
5

Проблема с реализацией последнего подхода с использованием фабричного метода, newCons, заключается в том, что вы пытаетесь изменить состояние объекта, используя setArg1(...), для которого, скорее всего, его поля должны быть установлены как final. Поскольку мы пытаемся сохранить как можно большую часть объекта неизменным, если не полностью, шаблон построителя решит эту проблему более правильно.

LordHieros
16 апреля 2018 в 13:31
2

Вы бы предпочли: public Cons () {this (madeUpArg1Value, madeUpArg2Value); }

Joshua Taylor
17 мая 2018 в 13:12
3

@ RodneyP.Barbati В Java конструкторы с меньшей арностью довольно часто вызывают конструкторы с большей арностью , а затем больше ничего не делают . если класс K имеет, например два последних поля a, b, тогда «общий конструктор» будет K(A a, B b) { this.a = a; this.b = b; }. Затем, если b имеет разумное значение по умолчанию, может быть конструктор с одним аргументом K(A a) { this(a, DEFAULT_B); }, а если есть также значение по умолчанию a, у нас есть конструктор по умолчанию: K() { this(DEFAULT_A); }. Это довольно распространенное соглашение в Java.

Joshua Taylor
17 мая 2018 в 13:14
0

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

avatar
Soni Vashisht
1 августа 2019 в 17:35
2

Да, вы можете вызывать конструкторы из другого конструктора. Например:

public class Animal {
    private int animalType;

    public Animal() {
        this(1); //here this(1) internally make call to Animal(1);
    }

    public Animal(int animalType) {
        this.animalType = animalType;
    }
}

вы также можете прочитать подробную информацию на сайте Объединение конструкторов в Java

avatar
ansh sachdeva
19 июня 2019 в 08:38
0

Я предпочитаю этот способ:

    class User {
        private long id;
        private String username;
        private int imageRes;

    public User() {
        init(defaultID,defaultUsername,defaultRes);
    }
    public User(String username) {
        init(defaultID,username, defaultRes());
    }

    public User(String username, int imageRes) {
        init(defaultID,username, imageRes);
    }

    public User(long id, String username, int imageRes) {
        init(id,username, imageRes);

    }

    private void init(long id, String username, int imageRes) {
        this.id=id;
        this.username = username;
        this.imageRes = imageRes;
    }
}
avatar
rogerdpack
9 апреля 2019 в 00:30
1

Первоначально от ансера Мирко Клемма, немного изменено, чтобы ответить на вопрос:

Просто для полноты: существует также блок инициализации экземпляра, который выполняется всегда и до вызова любого другого конструктора. Он состоит просто из блока операторов "{...}" где-то в теле определения вашего класса. У вас может быть даже больше одного. Вы не можете их вызывать, но они похожи на код «общего конструктора», если вы хотите повторно использовать некоторый код в конструкторах, аналогично вызываемым методам.

Так в вашем случае

{ 
  System.out.println("this is shared constructor code executed before the constructor");
  field1 = 3;
}

Существует также "статическая" версия этого для инициализации статических членов: "static {...}"

avatar
John McClane
29 июля 2018 в 01:14
4

Вы можете вызвать другой конструктор с помощью ключевого слова this(...) (когда вам нужно вызвать конструктор из того же класса) или ключевого слова super(...) (когда вам нужно вызвать конструктор из суперкласса).

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

avatar
Omar Faroque Anik
6 июня 2018 в 19:36
4

Это называется антипаттерном конструктора телескопирования или сцеплением конструктора. Да, вы определенно можете это сделать. Я вижу много примеров выше и хочу добавить, сказав, что если вы знаете, что вам нужны только два или три конструктора, это может быть нормально. Но если вам нужно больше, попробуйте использовать другой шаблон проектирования, например шаблон Builder. Например:

 public Omar(){};
 public Omar(a){};
 public Omar(a,b){};
 public Omar(a,b,c){};
 public Omar(a,b,c,d){};
 ...

Вам может понадобиться больше. В этом случае отличным решением будет паттерн-строитель. Вот статья, может быть полезно https://medium.com/@modestofiguereo/design-patterns-2-the-builder-pattern-and-the-telescoping-constructor-anti-pattern-60a33de7522e

avatar
Negi Rox
24 января 2018 в 05:45
4

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

    import java.util.*;
    import java.lang.*;

    class Test
    {  
        public static void main(String args[])
        {
            Dog d = new Dog(); // Both Calling Same Constructor of Parent Class i.e. 0 args Constructor.
            Dog cs = new Dog("Bite"); // Both Calling Same Constructor of Parent Class i.e. 0 args Constructor.

            // You need to Explicitly tell the java compiler to use Argument constructor so you need to use "super" key word
            System.out.println("------------------------------");
            Cat c = new Cat();
            Cat caty = new Cat("10");

            System.out.println("------------------------------");
            // Self s = new Self();
            Self ss = new Self("self");
        }
    }

    class Animal
    {
        String i;

        public Animal()
        {
            i = "10";
            System.out.println("Animal Constructor :" +i);
        }
        public Animal(String h)
        {
            i = "20";
            System.out.println("Animal Constructor Habit :"+ i);
        }
    }

    class Dog extends Animal
    {
        public Dog()
        {
            System.out.println("Dog Constructor");
        }
        public Dog(String h)
        {
            System.out.println("Dog Constructor with habit");
        }
    }

    class Cat extends Animal
    {
        public Cat()
        {
            System.out.println("Cat Constructor");
        }
        public Cat(String i)
        {
            super(i); // Calling Super Class Paremetrize Constructor.
            System.out.println("Cat Constructor with habit");
        }
    }

    class Self
    {
        public Self()
        {
            System.out.println("Self Constructor");
        }
        public Self(String h)
        {
            this(); // Explicitly calling 0 args constructor. 
            System.out.println("Slef Constructor with value");
        }
    }
avatar
GetBackerZ
21 ноября 2017 в 14:03
8

Довольно просто

public class SomeClass{

    private int number;
    private String someString;

    public SomeClass(){
        number = 0;
        someString = new String();
    }

    public SomeClass(int number){
        this(); //set the class to 0
        this.setNumber(number); 
    }

    public SomeClass(int number, String someString){
        this(number); //call public SomeClass( int number )
        this.setString(someString);
    }

    public void setNumber(int number){
        this.number = number;
    }
    public void setString(String someString){
        this.someString = someString;
    }
    //.... add some accessors
}

а теперь небольшой дополнительный кредит:

public SomeOtherClass extends SomeClass {
    public SomeOtherClass(int number, String someString){
         super(number, someString); //calls public SomeClass(int number, String someString)
    }
    //.... Some other code.
}

Надеюсь, это поможет.

avatar
Rodney P. Barbati
13 ноября 2017 в 23:01
6

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

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

class LambdaInitedClass {

   public LamdaInitedClass(Consumer<LambdaInitedClass> init) {
       init.accept(this);
   }
}

Позвоните с помощью ...

 new LambdaInitedClass(l -> { // init l any way you want });
avatar
S. Mayol
14 марта 2017 в 23:53
6

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

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

Методы или конструкторы сеттера

avatar
Akshay Gaikwad
3 марта 2017 в 09:27
8

Вызов конструктора из другого конструктора

class MyConstructorDemo extends ConstructorDemo
{
    MyConstructorDemo()
    {
        this("calling another constructor");
    }
    MyConstructorDemo(String arg)
    {
        System.out.print("This is passed String by another constructor :"+arg);
    }
}

Также вы можете вызвать родительский конструктор с помощью super() call

avatar
S R Chaitanya
7 января 2017 в 13:21
12

Да, можно вызывать один конструктор из другого. Но есть правило. Если вызов сделан из одного конструктора в другой, то

этот вызов нового конструктора должен быть первым оператором в текущем конструкторе

public class Product {
     private int productId;
     private String productName;
     private double productPrice;
     private String category;

    public Product(int id, String name) {
        this(id,name,1.0);
    }

    public Product(int id, String name, double price) {
        this(id,name,price,"DEFAULT");
    }

    public Product(int id,String name,double price, String category){
        this.productId=id;
        this.productName=name;
        this.productPrice=price;
        this.category=category;
    }
}

Значит, что-то подобное ниже не сработает.

public Product(int id, String name, double price) {
    System.out.println("Calling constructor with price");
    this(id,name,price,"DEFAULT");
}

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

public class SuperClass {
    public SuperClass() {
       System.out.println("Inside super class constructor");
    }
}
public class SubClass extends SuperClass {
    public SubClass () {
       //Even if we do not add, Java adds the call to super class's constructor like 
       // super();
       System.out.println("Inside sub class constructor");
    }
}

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

avatar
Shivanandam
21 ноября 2016 в 13:14
9

Я расскажу вам простой способ

Существует два типа конструкторов:

  1. Конструктор по умолчанию
  2. Параметризованный конструктор

Я объясню в одном примере

class ConstructorDemo 
{
      ConstructorDemo()//Default Constructor
      {
         System.out.println("D.constructor ");
      }

      ConstructorDemo(int k)//Parameterized constructor
      {
         this();//-------------(1)
         System.out.println("P.Constructor ="+k);       
      }

      public static void main(String[] args) 
      {
         //this(); error because "must be first statement in constructor
         new ConstructorDemo();//-------(2)
         ConstructorDemo g=new ConstructorDemo(3);---(3)    
       }
   }                  

В приведенном выше примере я показал 3 типа звонков

  1. вызов this () должен быть первым оператором в конструкторе
  2. Это объект без имени. это автоматически вызывает конструктор по умолчанию. 3. Это вызывает параметризованный конструктор.

Примечание: это должен быть первый оператор в конструкторе.

S R Chaitanya
7 января 2017 в 13:31
4

В основном методе у вас есть следующее: // this (); ошибка, потому что "должно быть первым оператором в конструкторе Этот оператор не имеет особого смысла. Если вы пытаетесь сказать, что this () не может быть вызван изнутри основного метода , тогда да, это не может быть, потому что main является статическим и не будет иметь ссылки на this ()

avatar
Utsav
16 ноября 2016 в 16:14
14

Да, в классе может присутствовать любое количество конструкторов, и они могут быть вызваны другим конструктором с помощью this() [Пожалуйста, не путайте вызов конструктора this() с ключевым словом this]. this() или this(args) должно быть первой строкой в ​​конструкторе.

Пример:

Class Test {
    Test() {
        this(10); // calls the constructor with integer args, Test(int a)
    }
    Test(int a) {
        this(10.5); // call the constructor with double arg, Test(double a)
    }
    Test(double a) {
        System.out.println("I am a double arg constructor");
    }
}

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

avatar
Akash Manngroliya
13 сентября 2016 в 05:48
7

Да, можно вызвать один конструктор из другого с использованием this()

class Example{
   private int a = 1;
   Example(){
        this(5); //here another constructor called based on constructor argument
        System.out.println("number a is "+a);   
   }
   Example(int b){
        System.out.println("number b is "+b);
   }
avatar
ABHISHEK RANA
27 ноября 2015 в 19:01
8

Вы можете создать конструктор из другого конструктора того же класса, используя ключевое слово this. Пример -

class This1
{
    This1()
    {
        this("Hello");
        System.out.println("Default constructor..");
    }
    This1(int a)
    {
        this();
        System.out.println("int as arg constructor.."); 
    }
    This1(String s)
    {
        System.out.println("string as arg constructor..");  
    }

    public static void main(String args[])
    {
        new This1(100);
    }
}

Выход - строка как конструктор arg .. Конструктор по умолчанию .. int как конструктор arg ..

avatar
amila isura
26 мая 2015 в 15:09
29

В конструкторе можно использовать ключевое слово this для вызова другого конструктора в том же классе. Это называется явным вызовом конструктора .

Вот еще один класс Rectangle, реализация которого отличается от реализации в разделе "Объекты".

public class Rectangle {
    private int x, y;
    private int width, height;

    public Rectangle() {
        this(1, 1);
    }
    public Rectangle(int width, int height) {
        this( 0,0,width, height);
    }
    public Rectangle(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

}

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

ANjaNA
18 июля 2015 в 02:58
1

почему бы вам не вызвать второй конструктор Rectangle(int width, int height) в Rectangle() вместо Rectangle(int x, int y, int width, int height)?

Wes
23 октября 2018 в 12:30
1

@ RodneyP.Barbati Я не могу согласиться с этим. Этот шаблон не допускает полей final.

avatar
olovb
7 мая 2015 в 22:52
18

Как все уже сказали, вы используете this(…), который называется явным вызовом конструктора .

Однако имейте в виду, что в таком явном заявлении вызова конструктора вы не можете ссылаться на

  • любые переменные экземпляра или
  • любые методы экземпляра или
  • любые внутренние классы , объявленные в этом классе или любом суперклассе, или
  • this или
  • super.

Как указано в JLS (§8.8.7.1).

avatar
Kaamel
23 апреля 2013 в 23:12
41

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

class MyClass {
   int field;


   MyClass() {
      init(0);
   } 
   MyClass(int value) {
      if (value<0) {
          init(0);
      } 
      else { 
          init(value);
      }
   }
   void init(int x) {
      field = x;
   }
}

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

class MyClass {
   int field;

   MyClass(int value) {
      if (value<0)
         field = 0;
      else
         field = value;
   }
   MyClass() {
      this(0);
   }
}
avatar
Christian Fries
11 марта 2013 в 20:33
226

[ Примечание: я просто хочу добавить один аспект, который я не видел в других ответах: как преодолеть ограничения требования, чтобы this () был в первой строке). ]

В Java другой конструктор того же класса может быть вызван из конструктора через this(). Однако обратите внимание, что this должен быть в первой строке.

public class MyClass {

  public MyClass(double argument1, double argument2) {
    this(argument1, argument2, 0.0);
  }

  public MyClass(double argument1, double argument2, double argument3) {
    this.argument1 = argument1;
    this.argument2 = argument2;
    this.argument3 = argument3;
  }
}

То, что this должно появиться в первой строке, похоже на большое ограничение, но вы можете создавать аргументы других конструкторов с помощью статических методов. Например:

public class MyClass {

  public MyClass(double argument1, double argument2) {
    this(argument1, argument2, getDefaultArg3(argument1, argument2));
  }

  public MyClass(double argument1, double argument2, double argument3) {
    this.argument1 = argument1;
    this.argument2 = argument2;
    this.argument3 = argument3;
  }

  private static double getDefaultArg3(double argument1, double argument2) {
    double argument3 = 0;

    // Calculate argument3 here if you like.

    return argument3;

  }

}
Software Engineer
8 июня 2016 в 23:00
16

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

Christian Fries
16 июня 2016 в 17:36
16

Я согласен с тем, что очень сложное преобразование, вероятно, указывает на проблему дизайна. Но 1) есть несколько простых преобразований, для которых это может быть полезно - не все конструкторы являются просто линейной проекцией на другие и 2) может быть другая ситуация, когда эта информация может стать ручной, например, поддержка устаревшего кода. (Хотя я согласен с вашим выводом, я не понимаю, почему это оправдывает голосование против).

Christian Fries
13 июня 2018 в 06:06
2

@ RodneyP.Barbati: Я вижу несколько проблем в том, чтобы делать это так, как вы это описываете: a) Делая это таким образом, невозможно проиллюстрировать использование статического метода в конструкторе (и это цель примера); -) и б) если вы делаете это по-своему, поля не могут быть final (конечные поля могут быть инициализированы только один раз).

Christian Fries
13 июня 2018 в 08:13
1

@ RodneyP.Barbati: Два других аспекта: c) Я считаю, что вы всегда должны выполнять инициализацию объекта в одной точке, которая должна быть самым общим конструктором. Если для инициализации объекта требуется сложная задача (инициализация объекта не является ленивой) или проверка или получение некоторых ресурсов (например, файла), то вам нравится делать это только один раз. И г) Добавление еще одного аргумента (например, аргумента 4), для которого инициализация зависит от значения аргумента 1 до аргумента 3, вам придется изменить все конструкторы в вашем случае, тогда как здесь вам нужно только добавить один и позволить 3-arg вызвать 4 -arg конструктор.

John McClane
29 июля 2018 в 01:34
0

Более общий способ преодоления ограничения «должен быть первым оператором в конструкторе» см. В этом ответе. Это применимо как к вызовам super(), так и к this().