Начнём с определения в википедии:

Полиморфизм в языках программирования и теории типов — способность функции обрабатывать данные разных типов

Существует два вида полиморфизма ad-hoc (он же мнимый) и параметрический (он же истинный). Язык Dart не поддерживает перегрузку (overloading) метода поэтому обсуждать ad-hoc нет смысла. Переходим к параметрическому полиморфизму и сразу смотрим на примере.

Создадим класс Teacher с 2-мя свойствами yearExpiriance - опыт в годах и birthYear - год рождение. Метод study где будем возвращать текст - Я уже обучаю студентов Х лет. А так же сеттор age - с помощью него будем устанавливать новую дату рождения и геттер - будем определять на сколько большой опыт преподавания у учителя.

class Teacher {
  final int yearExperience;
  late int yearBirth;

  Teacher({required this.yearExperience, required this.yearBirth});

  String study() {
    return 'I am teacher';
  }

  // Раскоментируйте, чтоб убедиться что перегрузку методов Дарт не поддерживает.
  // Вы получите ошибку - The name 'study' is already defined.
  // String study(int any) {
  //   return 'I study my students for $yearBirth';
  // }

  set age(int val) => yearBirth = (DateTime.now().year - val);

  String get isBigExpiriance => yearExperience >= 10
      ? 'Опыт больше или равен 10 лет'
      : 'Опыт меньше 10 лет';
}

Полиморфизм в ООП не может существовать без наследования!

Создаём два класса, EnglishTeacher - учитель английского языка, который будет наследоваться от Teacher и класс ChildrenEnglishTeacher (детский учитель английского языка), который будет наследоваться от EnglishTeacher.

import 'package:flutter_application_1/teacher.dart';

class EnglishTeacher extends Teacher {
  EnglishTeacher({required super.yearExperience, required super.yearBirth});
  // Переопределяем метод базового класса.
  @override
  String study() {
    return 'I have been teaching my students for $yearExperience years';
  }

  String hasLessonsToday() {
    return 'Yes. I have lesson today';
  }
  // Переопределяем метод базового класса.
  @override
  String get isBigExpiriance => yearExperience >= 5
      ? 'Большой опыт'
      : 'Опыт учителя английского языка  $yearExperience года';
}

Класс EnglishTeacher с помощью @override переопределяет метод study базового класса (Teacher) и будет возвращать другой текст, специфичный для учителя английского языка (Я обучаю моих студентов уже Х лет). А так же EnglishTeacher имеет собственный метод hasLessonsToday, который отвечает на вопрос: "Есть ли сегодня занятия?" и будет возвращать - "Да. Сегодня будет занятие". А так же мы, переопределили геттор isBigExpiriance, где изменили условия и тексты.

Класс ChildrenEnglishTeacher мы создадим, просто для того чтоб было понимание что можно построить иерархию классов, где каждый последующий класс будет наследоваться от вышестоящиего. И в самом нижнем классе всегда можно переопределить методы из всех вышестоящих в плоть то самого верхнего (базового Teacher). То есть метод hasLessons есть в классе EnglishTeacher, но его нет в базовом классе (самый верхний -Teacher).

А так же у нас есть возможность переопределить сеттор age который есть у самого верхнего базового класса Teacher.

import 'package:flutter_application_1/english_teacher.dart';

class ChildrenEnglishTeacher extends EnglishTeacher {
  ChildrenEnglishTeacher(
      {required super.yearExperience, required super.yearBirth});
  @override
  String hasLessonsToday() {
    return 'No. I have not';
  }

  @override
  set age(int val) => yearBirth = (DateTime.now().year - val - 3);
}

Теперь приступим к проверке, в функции main сделаем инстанс EnglishTeacher и запустим оба унаследованных от базового класса метода

void main() {
  final englishTeacher = EnglishTeacher(yearExperience: 5, yearBirth: 1970);
  print(englishTeacher.study());
  print(englishTeacher.isBigExpiriance);
}

Вывод в консоль

Оба метода успешно переопределились в дочернем классе
Оба метода успешно переопределились в дочернем классе

И проверим наш самый низший класс наследник ChildrenEnglishTeacher

void main() {
  final childrenEnglishTeacher =
      ChildrenEnglishTeacher(yearExperience: 6, yearBirth: 1920);
  childrenEnglishTeacher.age = 30;

  print(childrenEnglishTeacher.hasLessonsToday());
  print(childrenEnglishTeacher.yearBirth);
}

Вывод в консоли

Всё отработало правильно. Наш ChildrenEnglishTeacher переопределил метод hasLessonsToday от вышестоящего класса EnglishTeacher и так же переопределил слегка изменённый сеттор age от базового класса Teacher.

Итак, определение полиморфизма можно дать более понятными словами (после примера).

Принцип полиморфизма в ООП (объектно-ориентированном программировании) предполагает использование одного и того же имени метода или свойства для объектов разных классов. Иными словами, полиморфизм позволяет обращаться к объектам разных классов с помощью одних и тех же методов или свойств.

Зачем нам полиморфизм?

Прежде всего, чтоб убрать дублирование кода, улучшить читаемость и для удобного масштабирования.

Комментарии (5)


  1. lil_master
    15.01.2024 04:58

    birthYear - день рождение

    Наверное, год рождения?


    1. DartFlutter Автор
      15.01.2024 04:58

      Верно. Год рождения. Исправил.


  1. rsashka
    15.01.2024 04:58

    Полиморфизм в ООП не может существовать без наследования!

    Это только если ЯП не может переопределить метод класса без создания класса наследника. Если же язык программирования допускает такое переопределение (переопределить метод не создавая класс наследник), тогда может.


  1. IL_Agent
    15.01.2024 04:58

    Статья вводит в заблуждение. Наследование и override - это тоже ad-hoc. А параметрический полиморфизм - это скорее дженерики.


  1. sashker
    15.01.2024 04:58

    Для академических примеров можно Foo, Bar использовать, а то это выглядит как сарказм на тему учителя английского. И не хотелось бы, чтобы кто-то понял что такое полиморфизм, но совместно впитал анти-паттерны именования переменных.