Полиморфизм в Java
Last updated: 3 мая 2025 г.Полиморфизм — возможность использовать одно и то же имя для разных реализаций методов или конструкторов.
В Java полиморфизм реализуется с помощью перегрузки (overloading) и переопределения (overriding).
Перегрузка позволяет в пределах одного класса создавать несколько методов (или конструкторов) с одинаковым именем, а отличаться они будут колличеством или типом параметров.
Возникает закономерный вопрос: когда мы воспользуемся одним из таких методов с одинаковым именем, как компилятор определяет, какую реализацию выбрать? Ответ прост — он делает это на основании числа и типов переданных аргументов.
Рассмотрим пример перегрузки конструкторов:
1class Animal {
2 private String eats;
3 private int noOfLegs;
4
5 //Конструктор без параметров
6 //(называется конструктором по умолчанию).
7 public Animal(){}
8
9 //Конструктор с одним параметром
10 //задает значение только полю eats.
11 public Animal(String food){
12 this.eats = food;
13 }
14
15 //Конструктор с несколькими параметрами
16 //задает значения обоим полям.
17 public Animal(String food, int legs){
18 this.eats = food;
19 this.noOfLegs = legs;
20 }
21
22 //сеттеры и геттеры
23 public String getEats() {
24 return eats;
25 }
26
27 public void setEats(String eats) {
28 this.eats = eats;
29 }
30
31 public int getNoOfLegs() {
32 return noOfLegs;
33 }
34
35 public void setNoOfLegs(int noOfLegs) {
36 this.noOfLegs = noOfLegs;
37 }
38}
39
40class Polimorphism {
41 public static void main(String[] args) {
42 //Какой же из конструкторов
43 //Animal, которые мы определили будет использован
44 //при создании объекта животного?
45 //Ведь у них же всех одинаковые имена.
46
47 //На самом деле, можно просто выбрать
48 //нужный конструктор передав в него
49 //нужное количество параметров.
50 //То есть, как видим, в строке кода ниже
51 //вызывается конструктор с одним параметром.
52 Animal animal = new Animal("Grass");
53 //Значит будет использован конструктор
54 //public Animal(String food), ведь в конструктор
55 //передаётся всего один параметр.
56
57 //В выводе увидим, что строка Grass успешно
58 //записалась в поле объекта animal.
59 System.out.println(animal.getEats());
60 }
61}
Вывод:

Даже несмотря на то, что у всех конструкторов одинаковое имя Animal
, компилятор различает их по параметрам. Это и есть пример перегрузки конструктора, которая является одной из форм полиморфизма.
Однако помним, что полиморфизм может применяться не только к конструкторам, но и к методам.
Переопределение метода родителя в классе-наследнике
Под полиморфизмом также понимается переопределение метода родительского класса в классе-наследнике.
Например, ниже в классе Animal
(Животное) определён метод move()
(Движение), который просто сообщает, что животное может двигаться. Его реализация — вывод строки "The animal moves."
— является универсальной и подходит для любого животного. Такой метод вполне может использоваться без изменений в классах-наследниках.
Однако в некоторых случаях желательно уточнить поведение метода. Например, если мы создадим класс Cat
(Кот), который наследуется от Animal
, то он унаследует и метод move()
. Но в контексте кошки может быть полезно уточнить поведение этого метода, поскольку, разумеется, куда лучше, если move()
будет описывать именно движения, характерные для кошки — например, "The cat gracefully walks and jumps."
— а не просто общее "The animal moves."
В таких случаях метод move()
можно переопределить в классе-наследнике, чтобы сделать поведение более конкретным и реалистичным.
Это и есть проявление полиморфизма: один и тот же метод, но разная реализация в разных классах.
Рассмотрим пример:
1class Animal {
2 private String eats;
3 private int noOfLegs;
4
5 public Animal(){}
6 public Animal(String food){
7 this.eats = food;
8 }
9 public Animal(String food, int legs){
10 this.eats = food;
11 this.noOfLegs = legs;
12 }
13
14 public String getEats() {
15 return eats;
16 }
17
18 public void setEats(String eats) {
19 this.eats = eats;
20 }
21
22 public int getNoOfLegs() {
23 return noOfLegs;
24 }
25
26 public void setNoOfLegs(int noOfLegs) {
27 this.noOfLegs = noOfLegs;
28 }
29 // Метод с базовой реализацией, который,
30 // желательно, чтобы наследники переопределили.
31 public void move() {
32 System.out.println("The animal moves.");
33 }
34}
35
36class Cat extends Animal{
37 private String name;
38 private String color;
39
40 Cat(){}
41 Cat (String catName, String catColor) {
42 name = catName;
43 color = catColor;
44 System.out.println(color);
45 }
46
47 public String getName(){
48 return name;
49 }
50
51 public void setName(String catName){
52 name = catName;
53 }
54
55 public String getColor(){
56 return color;
57 }
58
59 public void setColor(String catColor){
60 color = catColor;
61 }
62
63 public void move(){
64 //Переопределяем метод move.
65 //Теперь движения кошки будут выводится как:
66 //"The cat gracefully walks and jumps.".
67 System.out.println("The cat gracefully walks and jumps.");
68 }
69}
70
71class Polimorphism2 {
72 public static void main(String[] args) {
73 Cat cat = new Cat();
74 // Воспользуемся методом move.
75 cat.move();
76 // Если бы метод move не был переопределён,
77 // на консоль вывелось бы "The animal moves."
78 // Но для кошки корректнее — "The cat gracefully
79 // walks and jumps."
80 }
81}
Вывод:

Каждое животное, наследующее класс Animal
, может по-своему переопределить метод move()
. Так, у птицы это может быть “летает”, у собаки — “бежит”, и так далее.
Переопределение методов — мощный механизм, позволяющий делать поведение объектов более точным и соответствующим их конкретному типу, оставаясь при этом в рамках единой абстракции.
Следующие уроки
Абстрактный класс
16
мин.
Интерфейсы в Java
13
мин.
Статические поля
10
мин.