Encodera

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

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

Оглавление

Полиморфизм

Полиморфизм — это способность одного и того же кода работать с объектами разных классов. Код обращается к объекту как к «животному», а какой именно звук прозвучит, решает сам объект.

Переменная типа родителя

В переменную типа суперкласса можно положить объект любого подкласса: собака — это животное. Но вызывается при этом версия метода самого объекта:

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

Результат:

Гав

Тип переменной — Animal, а объект в ней — Dog. Java выбирает метод по настоящему объекту во время выполнения программы.

Один цикл для разных объектов

Благодаря этому разные объекты можно сложить в один массив или список и обработать одинаково:

class Animal {
    String sound() {
        return "...";
    }
}
 
class Dog extends Animal {
    @Override
    String sound() {
        return "Гав";
    }
}
 
class Cat extends Animal {
    @Override
    String sound() {
        return "Мяу";
    }
}
 
void main() {
    Animal[] animals = { new Dog(), new Cat(), new Animal() };
    for (Animal a : animals) {
        IO.println(a.sound());
    }
}

Результат:

Гав
Мяу
...

Циклу не нужно знать, кто перед ним — собака или кошка.

Параметр типа родителя

Метод, который принимает суперкласс, подходит для всех подклассов, включая те, что появятся позже. Чтобы добавить новый вид фигуры, не придётся менять printArea():

class Shape {
    double area() {
        return 0;
    }
}
 
class Square extends Shape {
    double side;
 
    Square(double side) {
        this.side = side;
    }
 
    @Override
    double area() {
        return side * side;
    }
}
 
class Circle extends Shape {
    double radius;
 
    Circle(double radius) {
        this.radius = radius;
    }
 
    @Override
    double area() {
        return Math.PI * radius * radius;
    }
}
 
void printArea(Shape shape) {
    IO.println("Площадь: " + Math.round(shape.area()));
}
 
void main() {
    printArea(new Square(3));
    printArea(new Circle(2));
}

Результат:

Площадь: 9
Площадь: 13

Проверка типа: instanceof

Оператор instanceof проверяет, к какому классу относится объект. Если после имени класса указать переменную, Java сразу приведёт объект к этому типу:

class Animal {}
 
class Dog extends Animal {
    String fetch() {
        return "Принёс палку";
    }
}
 
void main() {
    Animal animal = new Dog();
    if (animal instanceof Dog dog) {
        IO.println(dog.fetch());
    }
}

Результат:

Принёс палку

Переменная dog существует только внутри if, когда проверка прошла. Проверки типа стоит использовать редко: чаще правильнее добавить общий метод в суперкласс.

Почему это удобно

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

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

Через переменную типа родителя видны только методы родителя. Метод, который есть лишь у подкласса, так вызвать нельзя, даже если в переменной лежит нужный объект:

class Animal {}
 
class Dog extends Animal {
    String fetch() {
        return "Принёс палку";
    }
}
 
void main() {
    Animal animal = new Dog();
    IO.println(animal.fetch());
}

Результат:

Main.java:11: error: cannot find symbol
    IO.println(animal.fetch());
                     ^

Компилятор смотрит на тип переменной, а не на объект. Используйте переменную типа Dog или проверку instanceof.

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

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