Ключевое слово super
1. Использование ключевого слова super
В сегодняшнем нашем уроке мы поговорим с вами о ключевом слове super в Java. Ключевое слово super очень похоже на ключевое слово this, которое мы с вами рассматривали в одном из предыдущих уроков. Ключевое слово super в Java используется тогда, когда подклассу требуется сослаться на его непосредственный супер класс.
У ключевого слова super имеются две общие формы:
- Для вызова конструктора супер класса. В этом случае после super мы используем круглые скобочки, в которых мы передаём список аргументов в наш суперкласс:
super(списокАргументов);
- И второй вариант используется для обращения к переменным либо методам суперкласса, чаще всего скрываемыми членами подкласса:
suреr.member;
2. Вызов конструкторов супер класса с помощью ключевого слова super
И давайте начнём рассмотрение с первого варианта.
Передача параметров в конструктор супер класса
Если в иерархии классов требуется передать параметры конструктору супер класса, то все подклассы должны передавать эти параметры вверх по иерархии. То есть из конструктора подкласса надо вызвать конструктор супер класса с помощью super()
.
В этом примере из конструктора класса HeavyBox
вызываем конструктора класса Box
с помощью super()
, тем самым передавая необходимые значения:
public class Box {
double width;
double height;
double depth;
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
public Box() {
}
}
public class HeavyBox extends Box {
int weight;
public HeavyBox(int width, int height, int depth, int weight) {
super(width, height, depth);
this.weight = weight;
}
public HeavyBox() {
this.weight = -1;
}
}
Если в конструкторе наследника нет явного вызова super()
, как например во втором конструкторе класса HeavyBox
, JVM сама его подставляет первой строкой:
public HeavyBox() {
super();
this.weight = -1;
}
Из этого следует, что супер класс должен иметь конструктор без параметров, иначе возникнет ошибка компиляции.
Вызов конструкторов в многоуровневой иерархии
Когда метод super()
вызывается из подкласса, вызывается конструктор его непосредственного супер класса. Это справедливо даже для многоуровневой иерархии. Давайте рассмотрим такой пример - допустим у нас в нашей иерархии есть три класса:
У нас есть SuperSuperClass, его расширяет SuperClass, и далее SomeClass расширяет SuperClass.
Давайте посмотрим на код этих классов:
public class SuperSuperClass {
public SuperSuperClass() {
System.out.println("В конструкторе SuperSuperClass");
}
}
public class SuperClass extends SuperSuperClass {
public SuperClass() {
System.out.println("В конструкторе SuperClass");
}
}
public class SomeClass extends SuperClass {
public SomeClass() {
System.out.println("В конструкторе SomeClass");
}
}
Код в этих классов достаточно краток. В классе SuperSuperClass есть конструктор, который просто выводит на консоль то, что мы находимся в конструкторе этого класса. В классе SuperClass есть аналогичный конструктор, и в классе SomeClass опять же есть только конструктор, который выводит на консоль информацию о том, что мы находимся в конструкторе SomeClass.
И давайте теперь создадим объект класса SomeClass:
SomeClass someClass = new SomeClass();
Теперь посмотрим в каком порядке у нас вызываются эти конструкторы:
В конструкторе SomeClass
В конструкторе SuperSuperClass
В конструкторе SuperClass
Несмотря на то, что мы создаём объект SomeClass, сначала у нас вызывается конструктор SuperSuperClass, потом конструктор SuperClass, и уже потом конструктор SomeClass. О чём это говорит? Это говорит о том, что сначала должны быть выполнены действия по конструированию самого первого суперкласса. Потом идём вверх по иерархии - выполняется действия по созданию объекта SuperClass, и уже потом мы возвращаемся к объекту SomeClass.
Использование super в Java 22, 23
До Java 22 вызов метода super, должен быть всегда в первом операторе в конструкторе. То есть, если мы напишем следующим образом, то у нас будет ошибка компиляции:
public class SomeClass extends SuperClass {
public SomeClass() {
System.out.println("В конструкторе SomeClass");// ошибка компиляции до Java 22
super();
}
}
Опять же - почему так? Потому что сначала должен вызваться конструктор суперкласса.
Но Java 22 была добавлена возможность написания кода перед super() для того, чтобы облегчить подготовку, валидацию и передачу аргументов в конструктор. В Java 22 эта фича была добавлена в форме preview и называлась Statements before Super. В Java 23 она была немножко изменена и переименована в Flexible Constructor Body, но всё равно это всё ещё preview. Preview означает то, что эта возможность ещё не вошла в язык Java, и для того чтобы её использовать Вы должны компилировать программу использовав специальный флаг enable preview.
То есть super() может быть теперь и второй строчкой. Но сразу нужно сказать, что не весь код можно писать перед super().
Выделяется пролог - это код до вызова super(). В предыдущем примере - это System.out.println. И эпилог - это код после вызова super() или же весь код в конструкторе.
В прологе МОЖНО выполнять любой код, который не обращается к создаваемому объекту:
-
инициализировать переменные классы
-
во внутреннем классе можно обращаться к членам внешнего (outer) класса
В прологе НЕЛЬЗЯ:
-
вызывать нестатические методы класса
-
создавать объекты нестатических внутренних классов (nonstatic inner)
Давайте более детально рассмотрим те случаи, когда в прологе мы можем писать какой-то код:
-
Для валидации аргументов, передаваемых в super()
-
Подготовка аргументов для конструктора суперкласса
-
Передача аргументов конструктору суперкласса
Первый вариант - это для валидации аргументов передаваемых в super().
Давайте вернёмся к классу HeavyBox:
public HeavyBox(int width, int height, int depth, int weight) {
if (width <= 0) {
throw new IllegalArgumentException();
}
super(width, height, depth);
this.weight = weight;
}
Представьте, что перед вызовом конструктора нужно проверить, что переменная width больше нуля. Если ширина меньше либо равна нулю, то необходимо выбросить исключение - в этом случае нет смысла создавать объект.
Второй вариант, когда это может быть необходимо - подготовка аргументов для передачи в конструктор суперкласса. Допустим, необходимо удвоить переменные width, height и depth перед вызовом конструктора:
public HeavyBox(int width, int height, int depth, int weight) {
width *= 2;
height *= 2;
depth *= 2;
super(width, height, depth);
this.weight = weight;
}
И третий вариант, где это может использоваться - передача аргументов конструктору суперкласса. Допустим, в классе Box есть конструктор, на вход которого передаются две переменные SomeClass:
public class Box {
...
public Box(SomeClass s1, SomeClass s2) {
}
...
}
И из HeavyBox вызывается этот конструктор. Если перед super() нельзя писать никакой код, то придётся создать два разных объекта:
public HeavyBox() {
super(new SomeClass(), new SomeClass());
}
Либо необходимо передавать эти объекты извне в HeavyBox. Но если есть возможность написать какой-то код перед super, то можно создать один объект и передавать его в качестве первого и второго аргумента:
public HeavyBox1() {
SomeClass s = new SomeClass();
super(s, s);
}
3. Обращения к члену супер класса с помощью ключевого слова super
И теперь давайте перейдем ко второй форме использования ключевого слова super - для обращения к членам суперкласса (к методам либо к переменным).
С помощью ключевого слова super
можно обратиться к члену супер класса из класса наследника. Чаще всего это можно сделать не используя super
, но в этом примере рассмотрим случаи, когда без него не обойтись.
В классе С
объявлена переменная i
типа int
. В его наследнике классе D
, тоже объявлена переменная i
, но типа String
. (Сразу же предупредим - на практике не стоит так делать! Пример приводится с целью иллюстрирования применение ключевого слова super
с переменными.) Из класса D
мы можем напрямую обратиться только к переменной String i
, которая перекрывает область видимости переменной int i
. Для обращения же к int i
, необходимо использовать слово super
.
Похожая ситуация и с методами. В обоих классах определен метод print()
. Если мы хотим из класса D
вызвать метод print()
класса С
, используем слово super
- super.print()
.
public class C {
public int i;
public void print() {
System.out.println("C.i = " + i);
}
}
public class D extends C {
public String i;
public D(String a, int b) {
i = a;
super.i = b;
}
public void print() {
System.out.println("D.i = " + i);
super.print();
}
}
public class UseSuperExample {
public static void main(String[] args) {
D d = new D("someString", 2);
d.print();
System.out.println(d.i);
}
}
Вывод на консоль:
D.i = someString
C.i = 2
someString
Презентацию с видео можно скачать на Patreon.
Курс 'Java для начинающих' на Udemy
Зарегистрируйтесь или войдите, чтобы иметь возможность оставить комментарий.