Питання Як я можу назвати одного конструктора з іншого в Java?


Чи можна викликати конструктор від іншого (в межах одного класу, а не від підкласу)? Якщо так, то як? І що може бути кращим способом викликати іншого конструктора (якщо є кілька способів це зробити)?


1828
2017-11-12 20:10


походження


перевірте це теж: yegor256.com/2015/05/28/one-primary-constructor.html - yegor256
Я вважаю, що посилка Вашого питання неправильна. Замість того, щоб викликати конструктор усередині конструктора, використовуйте заводський малюнок. Статичний фабричний спосіб спочатку створює всі об'єкти нижчого рівня. Потім він конструює об'єкти вищого рівня, які отримують повернення із фабричного виклику. Ця техніка усуває складність від моделі, яка підтримує підтримку, чіткість та тестування. - David Medinets


Відповіді:


Так, це можливо:

public class Foo {
    private int x;

    public Foo() {
        this(1);
    }

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

Для того, щоб ланцюжок до певного конструктора суперкласу замість одного в одному класі, використовуйте super замість this. Зауважте, що ви можете тільки ланцюжок до одного конструктора, і це повинно бути першим твердженням у вашому корпусі конструктора.

Дивись також це пов'язане питання, що стосується C #, але коли застосовуються однакові принципи.


2468
2017-11-12 20:12



Отже, я думав, що не можна викликати суперконструктора та іншого конструктора того ж класу, як обидва повинні бути першим рядком? - gsingh2011
@ gsingh2011: Дійсно. Ви можете тільки ланцюжок до один інший конструктор. - Jon Skeet
Це має з'явитися на першому рядку, але ви можете робити розрахунки в конструкторі перед його викликом: Ви можете використовувати статичні методи в аргументах цього () у першому рядку та інкапсулювати будь-який розрахунок, який повинен виконуватися перед дзвінком іншому конструктору в цьому статичному методі. (Я додав це як окрему відповідь). - Christian Fries
@ gsingh2011 Я знаю, що це пізно, але навпаки, ви можете викликати перевантажений конструктор за допомогою цього (...), а потім у цьому перевантаженому конструкторі ви можете зателефонувати конструктору базового класу за допомогою супер (...) - Ali
@JustinTime: Знову ж таки, це залежить від того, що ви маєте на увазі під "створенням" - об'єкт "створений" тим, що його пам'ять виділяється і тип встановлюється перед виконанням будь-яких конструкторів конструкторів. Конструктори - ініціалізація, аніж створення. Зокрема, тип об'єкту є його "кінцевим" типом прямо з самого початку - так, якщо ви викликаєте будь-які віртуальні методи від конструкторів, ви отримаєте найбільш специфічний перевизначення виклику. Я вважаю, що це відрізняється від C ++. - Jon Skeet


Використовуючи 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(asdf). Виклик супер повинен бути першим дзвінком у конструкторі або ви отримаєте помилку компілятора.


198
2018-03-11 20:33



Якщо використовуються численні параметри конструктора, розгляньте будівельника. Див. Пункт 2 "Ефективна Java" Джошуа Блоха. - koppor
Проблема з реалізацією останнього підходу з використанням заводського методу newCons, це те, що ви намагаєтеся змінити стан об'єкта, використовуючи setArg1(...), то, швидше за все, його поля будуть визначені як остаточні. Оскільки ми намагаємося зберегти якомога більше об'єкта, незмінного, а то й не повного, шаблон забудовника буде правильніше вирішувати це питання. - YoYo
Чи не краще б ви робити: public Cons () (це (madeUpArg1Value, madeUpArg2Value); } - LordHieros
Ініціалізація повинна відбуватися від найменшого до найбільшого - я ніколи не маю конструктора за замовчуванням викликати ланцюжок до багатопараметричного конструктора. Що повинно статися, це те, що всі конструктори називають стандартний або конструктор з меншими параметрами. - Rodney P. 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


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

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

  }

}

177
2018-04-23 23:12



Це правда, що ви можете викликати статичні методи таким способом, щоб виконати складні розрахунки для значень аргументів, що добре. Однак, якщо хтось вважає, що потрібен код перед делегуванням конструктора (this(...)), то було б розумно припустити, що десь була зроблена жахлива помилка, і, можливо, потребує трохи переосмислення дизайну. - Engineer Dollery
Я б погодився, що a дуже складне перетворення, ймовірно, вказує на проблему дизайну. Але 1) існують деякі прості перетворення, для яких це може бути корисним - не всі конструктори - це просто лінійна проекція для інших і 2) може існувати інша ситуація, коли ця інформація може стати рукою, як підтримка застарілого коду. (Хоча я погоджуюся з вашим висновком, я не розумію, чому це буде виправдати зменшення кількості голосів). - Christian Fries
Це абсолютно навпаки, що я хотів би запропонувати - конструктор без параметрів повинен ініціалізувати всі значення за замовчуванням. Конструктор 2 параметрів повинен викликати конструктор param, а потім ініціалізувати отримані 2 значення. Конструктор 3 параметрів повинен викликати конструктор 2 параметрів, а потім ініціалізувати 3-е значення до отриманого значення. Зробивши це, як показано, означає, що вам потрібно зробити ще більше роботи, щоб додати ще один параметр. - Rodney P. Barbati
@ РоднейП.Барбаті: Я бачу декілька проблем, роблячи це так, як ви це описали: а) робити це таким чином неможливо ілюструвати використання статичного методу в конструкторе (і це є наміром прикладу); -) і б) якщо ви зробите це своїм способом, поля не можуть бути final (остаточні поля можуть бути ініціалізовані лише один раз). - Christian Fries
@ РоднейП.Барбаті: два інших аспекти: в) я вважаю, що ви завжди повинні робити ініціалізацію об'єкта в одній точці, яка повинна бути найбільш загальним конструктором. Якщо ініціалізація об'єкта потребує складного завдання (об'єкт init не лінувався) або перевіряти або придбати деякі ресурси (наприклад, файл), то вам подобається це робити лише один раз. І г) Додавання іншого аргументу (скажімо аргументу 4), для якого ініціалізація залежить від значення аргументу1 до аргументу3, вам доведеться змінити всі конструктори у вашому випадку, тоді як тут вам потрібно лише додати один і дозволити 3-arg виклик 4 -arg конструктор. - Christian Fries


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

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

36
2018-05-26 15:09





В рамках конструктора ви можете використовувати 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;
    }

}

Цей клас містить набір конструкторів. Кожен конструктор ініціалізує деякі або всі змінні члена прямокутника.


23
2018-05-07 22:52



чому ти не називаєш другим конструктором, який є Rectangle(int width, int height) в Rectangle() замість Rectangle(int x, int y, int width, int height) ? - ANjaNA
Конструктор за замовчуванням не повинен знати конструкторів вищого рівня - це за замовчуванням. Після цього шаблону вам буде потрібно змінити один або декілька існуючих конструкторів, коли ви додасте нову. Наприклад, додайте значення lineWidth і подивіться, що я маю на увазі. Але за замовчуванням ініціалізуйте всі значення, а також поверніть ланцюжок конструктора, ви побачите кожен конструктор, який будує попередній, і ініціалізує тільки ті значення, які вона спеціально підтримує - Ви можете додати новий, не змінюючи існуючих. У Java є багато поширених шаблонів, які не є гарними шаблонами. - Rodney P. Barbati


Як всі вже сказали, ви використовуєте this(…), який називається явний виклик конструктора.

Однак майте на увазі це в такому явному операторі виклику конструктора ви можете не звертатися

  • будь-який змінні екземпляра або
  • будь-який приклади методів або
  • будь-який внутрішні класи заявлений у цьому класі або будь-якому суперкласі, або
  • this або
  • super.

Як зазначено в JLS (§8.8.7.1).


12
2017-11-21 13:14





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

Існує два види конструкторів:

  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. Це викликає Параметризований конструктор.

Примітка: це повинно бути першим твердженням у конструкторі.


7
2017-11-27 19:01



У вас є основний метод: // це (); помилка, оскільки "повинно бути першим твердженням у конструкторі  Це твердження не має сенсу. Якщо ви намагаєтеся це сказати це () не можна викликати зсередини основний method, then yes it can not be because main is static and will not reference to це () - S R Chaitanya
це те, що я передаю .. що нового у вашому коментарі @ SRChaitanya? - Shivanandam Sirmarigari
що ти передав це погано - Kevin Van Dyck


Ви можете використовувати конструктор іншого конструктора того ж класу за допомогою "цього" ключового слова. Приклад -

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 constructor .. Конструктор за замовчуванням int as arg конструктор ..


5
2018-03-03 09:27