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

1964

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

  • 2
    Я считаю, что предпосылка вашего вопроса неверна. Вместо вызова конструктора в конструкторе используйте шаблон Factory. Статический метод фабрики сначала создает все объекты более низкого уровня. Затем он создает объекты более высокого уровня, которые получают возврат от вызова фабрики. Этот метод устраняет сложность модели, которая помогает в обслуживании, ясности и тестировании.
  • 0
    Я обычно переключался на частные конструкторы и фабричные методы, поскольку конструкторы из-за их ограничений нарушают принцип открытого-закрытого типа. Я думаю, что этот комментарий должен быть правильным ответом, все остальное запутает ваших товарищей по команде.
Теги:
constructor

18 ответов

2755
Лучший ответ

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

public class Foo {
    private int x;

    public Foo() {
        this(1);
    }

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

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

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

  • 22
    Итак, я предположил, что невозможно вызвать супер-конструктор и другой конструктор того же класса, так как оба должны быть в первой строке?
  • 22
    @ gsingh2011: Действительно. Вы можете связать только один другой конструктор.
Показать ещё 15 комментариев
213

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

  • 24
    Если используется много параметров конструктора, рассмотрим конструктор. См. Пункт 2 «Эффективной Явы» Джошуа Блоха.
  • 5
    Проблема с реализацией последнего подхода с использованием фабричного метода, newCons , заключается в том, что вы пытаетесь изменить состояние объекта, используя setArg1(...) , для которого, скорее всего, его поля должны быть установлены как final. Поскольку мы пытаемся сохранить как можно больше объекта неизменным, если не полностью, шаблон компоновщика будет решать эту проблему более корректно.
Показать ещё 5 комментариев
178

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

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

  }

}
  • 8
    Это правда, что вы можете вызывать статические методы таким образом, чтобы выполнять сложные вычисления для значений аргументов, и это нормально. Однако, если вы чувствуете, что код необходим перед делегированием конструктора ( this(...) ), тогда было бы разумно предположить, что где-то была совершена ужасная ошибка и что дизайн, возможно, нуждается в некотором пересмотре.
  • 12
    Я согласен, что очень сложное преобразование, скорее всего, указывает на проблему дизайна. Но 1) существуют некоторые простые преобразования, для которых это может быть полезно - не все конструкторы представляют собой просто линейную проекцию на других, и 2) могут быть другие ситуации, когда эта информация может стать рукой, например, поддержка устаревшего кода. (Хотя я согласен с вашим выводом, я не понимаю, почему это оправдало бы отрицательное голосование).
Показать ещё 4 комментария
37

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

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

Внутри конструктора вы можете использовать ключевое слово 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;
    }

}

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

  • 1
    почему бы вам не вызвать второй конструктор Rectangle(int width, int height) в Rectangle() вместо Rectangle(int x, int y, int width, int height) ?
  • 0
    Конструктор по умолчанию не должен обладать знаниями о конструкторах более высокого уровня - он используется по умолчанию. Следование этому шаблону приведет к необходимости изменения одного или нескольких существующих конструкторов при добавлении нового. Например, добавьте значение lineWidth и посмотрите, что я имею в виду. Но по умолчанию инициализируйте все значения и переверните цепочку конструктора, вы увидите, что каждый конструктор строит предыдущий и инициализирует только те значения, которые он специально поддерживает - вы можете добавить новое, не изменяя существующие. Есть много общих шаблонов в Java, которые не являются хорошими шаблонами.
Показать ещё 1 комментарий
14

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

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

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

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

10

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

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

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 constructor like 
       // super();
       System.out.println("Inside sub class constructor");
    }
}

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

10

Да, любое число конструкторов может присутствовать в классе, и их можно вызвать другим конструктором, используя this() [Пожалуйста, не путайте 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

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

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

  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. этот вызов() должен быть первым выражением в конструкторе
  2. Это имя меньше объекта. это автоматически вызывает конструктор по умолчанию. 3. Он вызывает конструктор Parameterized.

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

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

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

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

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

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

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

5

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

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

class LambdaInitedClass {

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

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

 new LambdaInitedClass(l -> { // init l any way you want });
5

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

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

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

4

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

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

4

Это называется привязкой к троекрату или цепочке конструкторов Telescoping Constructor. Да, вы можете это сделать. Я вижу много примеров выше, и я хочу добавить, сказав, что если вы знаете, что вам нужен только два или три конструктора, возможно, все в порядке. Но если вам нужно больше, попробуйте использовать другой шаблон дизайна, такой как шаблон 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

Я знаю, что есть так много примеров этого вопроса, но то, что я нашел, я помещаю здесь, чтобы поделиться своей идеей. существует два способа построения конструктора. В том же классе вы можете использовать это ключевое слово. в Inheritance вам нужно использовать ключевое слово 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");
        }
    }
4

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

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

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

  • 0
    Мне не нравится этот подход, потому что вызов одного из конструкторов оставляет одно из значений неинициализированным - другими словами, вы получаете недопустимый объект. Любой из конструкторов с одним параметром должен вызывать конструктор с двумя параметрами, передавая null для одного из значений. Таким образом, вы гарантированно правильно инициализируете все значения в одном месте.
  • 0
    @ RodneyP.Barbati Если я инициализирую переменные, это улучшит проблему, к которой вы обращаетесь?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню