Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Наследование

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

extends

Класс-наследник объявляют с ключевым словом extends. Родителя называют суперклассом, а наследника — подклассом:

class Animal {
    String name;
 
    String describe() {
        return name + " — животное";
    }
}
 
class Dog extends Animal {
    String bark() {
        return name + ": Гав!";
    }
}
 
void main() {
    Dog dog = new Dog();
    dog.name = "Рекс";
    IO.println(dog.describe());
    IO.println(dog.bark());
}

Результат:

Рекс — животное
Рекс: Гав!

У Dog нет своего поля name и метода describe() — они пришли от Animal.

Конструктор и super(...)

Конструкторы не наследуются. Конструктор подкласса вызывает конструктор родителя через super(...), передавая ему нужные данные:

class Animal {
    protected String name;
 
    Animal(String name) {
        this.name = name;
    }
}
 
class Cat extends Animal {
    Cat(String name) {
        super(name);
    }
 
    String meow() {
        return name + ": Мяу";
    }
}
 
void main() {
    IO.println(new Cat("Мурка").meow());
}

Результат:

Мурка: Мяу

Модификатор protected открывает поле для подклассов и для классов того же пакета, но не для всего остального кода. Пакеты разберём в конце главы.

Переопределение методов

Подкласс может объявить метод с той же сигнатурой, что у родителя, — тогда вызывается его версия. Это называется переопределением. Аннотация @Override просит компилятор проверить, что такой метод у родителя действительно есть:

class Animal {
    String sound() {
        return "...";
    }
}
 
class Dog extends Animal {
    @Override
    String sound() {
        return "Гав";
    }
}
 
void main() {
    IO.println(new Animal().sound());
    IO.println(new Dog().sound());
}

Результат:

...
Гав

Вызов версии родителя

Через super.метод() подкласс вызывает родительскую версию метода и дополняет её, а не пишет всё заново:

class Product {
    String label() {
        return "Товар";
    }
}
 
class Book extends Product {
    @Override
    String label() {
        return super.label() + " / Книга";
    }
}
 
void main() {
    IO.println(new Book().label());
}

Результат:

Товар / Книга

Один родитель и класс Object

В Java у класса может быть только один прямой родитель. Если extends не указан, родителем становится класс Object — корень всей иерархии. Поэтому у любого объекта есть методы toString() и equals(): они унаследованы от Object. Класс с модификатором final запрещает наследование от себя — так устроен, например, String.

Частая ошибка

Если у родителя есть только конструктор с параметрами, подкласс обязан вызвать его через super(...). Без этого Java пытается вызвать конструктор без параметров, которого нет:

class Animal {
    String name;
 
    Animal(String name) {
        this.name = name;
    }
}
 
class Dog extends Animal {
    Dog() {
        IO.println("Создаём собаку");
    }
}
 
void main() {
    new Dog();
}

Результат:

Main.java:10: error: constructor Animal in class Main.Animal cannot be applied to given types;
    Dog() {
          ^

Добавьте super("Рекс"); или передайте имя через параметр конструктора Dog.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.