Статьи

Java Q / A: конструктор цепочки

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

Что такое конструктор цепочки?

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

Можете ли вы показать мне пример?

Да.

import java.util.*;
import java.lang.*;
import java.io.*;
 
class Ideone
{
  class Parent {
    public Parent() {
      System.out.println("This is parent");
    }
  }
 
  class Child extends Parent {
    public Child() {
      System.out.println("This is child");
    }
  }
 
  public static void main (String[] args)
  {
    Ideone one = new Ideone();
    Child c = one.new Child();
  }
}

Если вы запустите приведенный выше пример , вы увидите следующий вывод:

Это родитель
это ребенок

Как вы можете видеть, компилятор неявно вызывал конструктор no-arg родительского элемента дочернего элемента, что привело к приведенному выше выводу.

Что произойдет, если родительский класс не имеет конструктора?

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

Что произойдет, если родительский класс не имеет конструктора без аргументов, например:

01.
importjava.util.*;
02.
importjava.lang.*;
03.
importjava.io.*;
04.
  
05.
classIdeone
06.
{
07.
  classParent {
08.
    publicParent(String hello) {
09.
      System.out.println(hello);
10.
    }
11.
  }
12.
  
13.
  classChild extendsParent {
14.
    publicChild() {
15.
      System.out.println("This is child");
16.
    }
17.
  }
18.
  
19.
  publicstaticvoidmain (String[] args)
20.
  {
21.
    Ideone one = newIdeone();
22.
    Child c = one.newChild();
23.
  }
24.
}

Ошибка времени компиляции будет выдана,  если вы явно не вызовете ни один из доступных родительских конструкторов, потому что в этом случае компилятор пытается автоматически вызвать конструктор no-arg родительского элемента, и, так как он не имеет, ошибка произойдет.

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

Нет.

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

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

Это один из двух способов ее решения. Другой дан ниже —

01.
importjava.util.*;
02.
importjava.lang.*;
03.
importjava.io.*;
04.
  
05.
classIdeone
06.
{
07.
  classParent {
08.
    publicParent(String hello) {
09.
      System.out.println(hello);
10.
    }
11.
  }
12.
  
13.
  classChild extendsParent {
14.
    publicChild() {
15.
      super("Hi Parent!");
16.
      System.out.println("This is child");
17.
    }
18.
  }
19.
  
20.
  publicstaticvoidmain (String[] args)
21.
  {
22.
    Ideone one = newIdeone();
23.
    Child c = one.newChild();
24.
  }
25.
}

Теперь вы не получите никакой ошибки!

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

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

Что это()?

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

01.
importjava.util.*;
02.
importjava.lang.*;
03.
importjava.io.*;
04.
  
05.
classIdeone
06.
{
07.
  classAClass {
08.
    publicAClass() {
09.
      this("Say Hi!");
10.
      System.out.println("This is default");
11.
    }
12.
  
13.
    publicAClass(String hi) {
14.
      System.out.println(hi);
15.
      System.out.println("This is with one arg");
16.
    }
17.
  }
18.
  
19.
  publicstaticvoidmain (String[] args)
20.
  {
21.
    Ideone one = newIdeone();
22.
    AClass c = one.newAClass();
23.
  }
24.
}

Если вы запустите приведенный выше пример , вы увидите —

Say Hi!
This is with one arg
This is default

Если я использую this () внутри конструктора для вызова другого конструктора, будет ли родительский конструктор вызываться дважды?

Неа —

01.
importjava.util.*;
02.
importjava.lang.*;
03.
importjava.io.*;
04.
  
05.
classIdeone
06.
{
07.
  classParent {
08.
    publicParent() {
09.
      System.out.println("Greetings, underlings!");
10.
    }
11.
  }
12.
  
13.
  classChild extendsParent {
14.
    publicChild() {
15.
      this("Hi single-arg child!");
16.
      System.out.println("This is no-arg child");
17.
    }
18.
  
19.
    publicChild(String sayHi) {
20.
      System.out.println(sayHi);
21.
      System.out.println("This is single-arg child");
22.
    }
23.
  }
24.
  
25.
  publicstaticvoidmain (String[] args) throwsjava.lang.Exception
26.
  {
27.
    Ideone one = newIdeone();
28.
    Child c = one.newChild();
29.
  }
30.
}

Выход вышеупомянутой программы  —

Greetings, underlings!
Hi single-arg child!
This is single-arg child
This is no-arg child

Мы знаем, что Object неявно расширяется каждым классом в Java. Означает ли это, что его конструктор без аргументов также вызывается во время конструирования?

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

Я сонный. Было бы неплохо подвести итоги нашего разговора на данный момент.

Конечно.

  • Когда у родителя нет / только конструктор по умолчанию без аргументов —
  1. If the child constructor does not explicitly invoke the parent constructor, then the compiler inserts one call to the default parent constructor as the first line of the child constructors.
  2. If the child explicitly calls the parent constructor, then no automatic call to parent constructor is issued.
  • When the parent has default/no-arg constructor as well as other overloaded constructor(s) –
  1. If the child constructor does not explicitly invoke the parent constructor, then the compiler inserts one call to the default parent constructor as the first line of the child constructors.
  2. If the child explicitly calls any of the parent constructors (does not matter which one) then no automatic calls to parent is issued.
  • When the parent has no default/no-arg constructors, that means it has explicit constructors and all of them requires arguments –
  1. If the child does not explicitly invoke any of the existing parent constructors, then a compile time error is issued.
  2. If the child constructor explicitly calls any of the existing parent constructors then the program continues without any error.

Thank you. Let’s have this type of conversation again. Good night.

Sure. Anytime.

Good night to you too.