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

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

12.11.2008 20:10:19
Я считаю, что предпосылка вашего вопроса неверна. Вместо вызова конструктора в конструкторе используйте шаблон Factory. Статический метод фабрики сначала создает все объекты более низкого уровня. Затем он создает объекты более высокого уровня, которые получают возврат от вызова фабрики. Этот метод устраняет сложность модели, которая помогает в обслуживании, ясности и тестировании.
David Medinets 6.07.2018 19:49:17
Я обычно переключался на частные конструкторы и фабричные методы, поскольку конструкторы из-за их ограничений нарушают принцип открытого-закрытого типа. Я думаю, что этот комментарий должен быть правильным ответом, все остальное запутает ваших товарищей по команде.
Richard 21.01.2019 12:27:45
Срой, но это не очень хорошая практика, если вы хотите сделать что-то подобное, перегружайте конструктор. Если вы хотите обернуть содержимое, это можно сделать, но для другого перелива. Не конструктор публичного класса Foo {private int x; public Foo () {} public Foo (int x) {this.x = x; } public Foo (int x, int y) {this.x = x; this.y = у}
Augusto 24.07.2019 19:16:16
21 ОТВЕТ
РЕШЕНИЕ

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

public class Foo {
    private int x;

    public Foo() {
        this(1);
    }

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

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

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

2952
23.05.2017 12:34:53
Итак, я предположил, что невозможно вызвать супер-конструктор и другой конструктор того же класса, так как оба должны быть в первой строке?
gsingh2011 2.11.2012 18:02:31
@ gsingh2011: Действительно. Вы можете связать только один другой конструктор.
Jon Skeet 2.11.2012 18:06:07
Это должно появиться в первой строке, но вы можете выполнять вычисления в конструкторе до его вызова: вы можете использовать статические методы в аргументах this () в первой строке и инкапсулировать любые вычисления, которые должны быть выполнены до вызова другому конструктору в этом статическом методе. (Я добавил это как отдельный ответ).
Christian Fries 11.03.2013 20:34:27
@ gsingh2011 Я знаю, что уже поздно, но, наоборот, вы можете вызвать перегруженный конструктор, используя это (...), а затем в этом перегруженном конструкторе вы можете вызвать конструктор базового класса, используя super (...)
Ali 13.05.2013 07:23:05
@JustinTime: Опять же, это зависит от того, что вы подразумеваете под «созданием» - объект «создается» в том смысле, что его память выделяется, а тип устанавливается перед выполнением любых тел конструктора. Конструкторы - это инициализация, а не создание. В частности, тип объекта является его «конечным» типом с самого начала - поэтому, если вы вызываете какие-либо виртуальные методы из конструкторов, вы получите наиболее специфичное вызываемое переопределение. Я считаю, что это отличается от C ++.
Jon Skeet 9.02.2016 19:06:42

Используя 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 или просто «of»:

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 должен быть первым вызовом в конструкторе, иначе вы получите ошибку компилятора.

247
26.08.2019 09:35:48
Если используется много параметров конструктора, рассмотрим конструктор. См. Пункт 2 «Эффективной Явы» Джошуа Блоха.
koppor 13.11.2012 20:16:00
Проблема с реализацией последнего подхода, использующего фабричный метод, newConsсостоит в том, что вы пытаетесь изменить состояние объекта, используя setArg1(...), для которого, скорее всего, его поля должны быть установлены как конечные. Поскольку мы пытаемся сохранить как можно больше объекта неизменным, если не полностью, шаблон компоновщика будет решать эту проблему более корректно.
YoYo 30.01.2016 06:46:25
Не лучше ли: public public Cons () {this (madeUpArg1Value, madeUpArg2Value); }
LordHieros 16.04.2018 13:31:23
Инициализация должна проходить от наименьшего к наибольшему - у меня никогда не было бы конструктора по умолчанию, вызывающего цепочку к многопараметрическому конструктору. Что должно произойти, так это то, что все конструкторы вызывают либо конструктор по умолчанию, либо конструктор с меньшим количеством параметров.
Rodney P. Barbati 23.04.2018 18:17:23
@ RodneyP.Barbati В Java довольно часто для конструкторов с меньшим числом аргументов вызывать конструкторы с большим числом аргументов, а затем ничего не делать . если класс K имеет, например, два конечных поля a, b, тогда «общим конструктором» будет K(A a, B b) { this.a = a; this.b = b; }. Тогда, если bесть то по умолчанию, может быть один-Arg конструктор K(A a) { this(a, DEFAULT_B); }, и если есть по умолчанию , aа также, у нас есть конструктор по умолчанию: K() { this(DEFAULT_A); }. Это довольно распространенное соглашение в Java.
Joshua Taylor 17.05.2018 13:12:55

[ Примечание: я просто хочу добавить один аспект, который я не видел в других ответах: как преодолеть ограничения требования, что 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;

  }

}
205
10.02.2016 02:53:05
Это правда, что вы можете вызывать статические методы таким образом, чтобы выполнять сложные вычисления для значений аргументов, и это нормально. Однако, если вы чувствуете, что код необходим перед делегированием конструктора ( this(...)), тогда было бы разумно предположить, что где-то была сделана ужасная ошибка и что дизайн, возможно, нуждается в некотором пересмотре.
Software Engineer 8.06.2016 23:00:08
Я согласен, что очень сложное преобразование, скорее всего, указывает на проблему дизайна. Но 1) существуют некоторые простые преобразования, для которых это может быть полезно - не все конструкторы представляют собой просто линейную проекцию на других, и 2) может быть другая ситуация, когда эта информация может стать рукой, например, поддержка устаревшего кода. (Хотя я согласен с вашим выводом, я не понимаю, почему это оправдывает отрицательное голосование).
Christian Fries 16.06.2016 17:36:43
Это полностью противоположно тому, что я хотел бы предложить - конструктор без параметров должен инициализировать все значения по умолчанию. Конструктор 2 параметров должен вызвать конструктор no param, а затем инициализировать 2 полученных значения. Конструктор с 3 параметрами должен вызвать конструктор с 2 параметрами, а затем инициализировать 3-е значение полученным значением. Выполнение, как показано, означает, что вам нужно проделать гораздо больше работы, чтобы добавить другой параметр.
Rodney P. Barbati 23.04.2018 18:22:35
@ RodneyP.Barbati: Я вижу несколько проблем в том, чтобы сделать это так, как вы это описываете: a) Делая это таким образом, невозможно проиллюстрировать использование статического метода в конструкторе (и это является целью примера); -) и б) если вы делаете это по-своему, поля не могут быть final(окончательные поля могут быть инициализированы только один раз).
Christian Fries 13.06.2018 06:06:33
@ RodneyP.Barbati: Два других аспекта: c) Я считаю, что вы всегда должны выполнять инициализацию объекта в одной точке, которая должна быть наиболее общим конструктором. Если для инициализации объекта требуется сложная задача (инициализация объекта не является ленивой) или проверка или получение некоторых ресурсов (например, файла), то вам нравится делать это только один раз. И d) добавив еще один аргумент (скажем, аргумент 4), для которого инициализация зависит от значения аргумента 1, до аргумента 3, вам придется изменить все конструкторы в вашем случае, тогда как здесь вам нужно только добавить один и позволить 3-аргументу вызвать 4 -арг конструктор.
Christian Fries 13.06.2018 08:13:28

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

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);
   }
}
39
23.04.2013 23:12:24

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

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

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

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

15
7.05.2015 22:52:03

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

Вот еще один класс Rectangle, реализация которого отличается от реализации в разделе Objects.

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;
    }

}

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

26
12.02.2016 01:50:32
почему бы вам не называть второй конструктор , который Rectangle(int width, int height)в Rectangle()вместо Rectangle(int x, int y, int width, int height)?
ANjaNA 18.07.2015 02:58:04
Конструктор по умолчанию не должен обладать знаниями о конструкторах более высокого уровня - это значение по умолчанию. Следование этому шаблону приведет к необходимости изменения одного или нескольких существующих конструкторов при добавлении нового. Например, добавьте значение lineWidth и посмотрите, что я имею в виду. Но по умолчанию инициализируйте все значения и переверните цепочку конструктора, вы увидите, что каждый конструктор строит предыдущий и инициализирует только те значения, которые он специально поддерживает. Вы можете добавить новое, не изменяя существующие. Есть много общих шаблонов в Java, которые не являются хорошими шаблонами.
Rodney P. Barbati 29.05.2018 16:21:01
@ RodneyP.Barbati Я не могу согласиться в этом случае. Этот шаблон не учитывает окончательные поля.
Wes 23.10.2018 12:30:55

Вы можете создать конструктор из другого конструктора того же класса, используя ключевое слово "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 ..

7
27.11.2015 19:01:24

Да можно вызывать один конструктор из другого с использованием 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);
   }
6
11.04.2018 02:20:50

Да, в классе может присутствовать любое количество конструкторов, и они могут быть вызваны другим конструктором, используя 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");
    }
}

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

9
21.06.2018 05:46:06

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

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

  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. Это вызывает параметризованный конструктор.

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

8
19.12.2017 05:55:52
В методе main у вас есть следующее: // this (); ошибка, потому что "должен быть первым оператором в конструкторе. Это утверждение не имеет особого смысла. Если вы пытаетесь сказать, что this () не может быть вызвано из метода main , тогда да, это не может быть, потому что main является статическим и не будет иметь ссылки на this ()
S R Chaitanya 7.01.2017 13:31:52
@ SR Чайтанья - он говорит, что строка кода, которая содержит это (); должна быть первой строкой в ​​переопределенном конструкторе. Вы также правы, что ключевое слово this не имеет смысла в статическом методе.
Rodney P. Barbati 22.01.2019 08:57:48

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

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

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");
    }
}

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

11
22.08.2018 17:30:59

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

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

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

7
3.03.2017 09:50:18

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

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

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

5
23.05.2017 12:02:58

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

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

class LambdaInitedClass {

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

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

 new LambdaInitedClass(l -> { // init l any way you want });
5
13.11.2017 23:01:22

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

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.
}

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

7
23.01.2019 15:53:39
Мне не нравится этот подход, потому что вызов одного из конструкторов оставляет одно из значений неинициализированным - другими словами, вы получаете недопустимый объект. Любой из конструкторов с одним параметром должен вызывать конструктор с двумя параметрами, передавая null для одного из значений. Таким образом, вы гарантированно правильно инициализируете все значения в одном месте.
Rodney P. Barbati 22.01.2019 08:51:30
@ RodneyP.Barbati Если я инициализирую переменные, это улучшит проблему, к которой вы обращаетесь?
GetBackerZ 23.01.2019 15:49:43

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

    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");
        }
    }
3
13.02.2018 14:05:23

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

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

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

4
6.06.2018 19:36:32

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

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

4
29.07.2018 01:14:04

Первоначально от anser Мирко Клемм, немного изменен для решения вопроса:

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

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

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

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

1
24.05.2019 16:57:54

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

    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;
    }
}
0
19.06.2019 08:38:36

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

public class Animal {
    private int animalType;

    public Animal() {
        this(1);
    }

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

Вы также можете прочитать подробно из Конструктора Цепочки в Java

1
14.08.2019 12:01:43