Что такое covariant в Dart

О чем статья

Ключевое слово covariant было внедрено в Dart для борьбы с важной проблемой переопределения методов.

В статье содержатся разбор проблемы, описание работы covariant и пример его использования для решения проблемы.

Проблема

Если в дочернем классе переопределяется метод родительского класса, то параметр метода дочернего класса может быть экземпляром только того класса, что указан в данного параметра в методе родительского класса.

Т.о., если в переопределяемом методе использовать параметр класса, который является дочерним для класса, который указан для параметра в методе наследуемого класса, то возникнет ошибка. Пример представлен на Рис. 1.

Рис.1 Пример проблемы

Рис. 1 Пример проблемы

  • Cat — это класс-наследник Animal, который переопределяет метод eat. Animal в методе eat требует параметр типа данных Food. 

  • Fish — это подкласс Food — т.е. неявно экземпляры класса Fish являются экземплярами класса Food. Однако, если в Cat.eat указать параметр с типом данных Fish, то возникнет ошибка.

Аналогичная ситуация с реализацией интерфейса (см. Рис. 2).

Рис.2 - Пример с интерфейсом

Рис. 2 — Пример с интерфейсом

Чтобы решить эту проблему, в Dart 2.12 было введено ключевое слово covariant.

Что такое covariant

covariant — это ключевое слово, которое используется вместе с параметрами методов при переопределении в дочерних классах методов родительских классов для того, чтобы решить проблему нераспознавания дочерних классов при переопределении методов.

Параметр дочернего метода вместо типа данных, указанного в переопределяемом методе родительского класса, может использовать тип данных, который является дочерним для указанного в методе родительского класса. Пример представлен на Рис. 3.

Рис.3 - Пример использования covariant в дочернем классе

Рис. 3 — Пример использования covariant в дочернем классе

Ключевое слово covariant сообщает анализатору то, что не нужно осуществлять строгую проверку типа данных. Однако, если использовать для параметра тип данных, который не является дочерним классом класса, указанного для параметра в методе родительского класса, то возникнет ошибка (см. Рис. 4).

Рис.4 - Пример ошибки

Рис. 4 — Пример ошибки

Ключевое слово covariant может быть использовано в методе суперкласса или в методе производного класса. Однако, хорошей практикой считается использование covariant в методах родительского класса, чтобы в интерфейсе родительского класса явно указать то, что дочерние классы могут изменять этот интерфейс. Также такой подход позволяет всем дочерним классам в переопределении метода изменять тип данных для параметра, определенного с covariant. Пример представлен на Рис. 5.

Рис.5 - Пример использования covariant в родительском классе

Рис. 5 — Пример использования covariant в родительском классе

Заключение

Ключевое слово covariant знать полезно, ведь что-то аналогичное примеру, представленному в статье, очень даже может встретиться в реальной жизни. Также covariant часто используется в исходниках Flutter и его не редко спрашивают на собеседованиях.

Благодарю за внимание!

Код из примеров

class Food {}

class Animal {
  void eat(covariant Food food) {
    print('Animal eats $food');
  }
}

class Fish extends Food {}

class Cat extends Animal {
  @override
  void eat(Fish food) {
    print('Cat eats $food');
  }
}

class Meat extends Food {}

class Dog extends Animal {
  @override
  void eat(Meat food) {
    print('Dog eats $food');
  }
}

void main() {
  Food food = Food();
  Animal animal = Animal();
  animal.eat(food);

  Fish fish = Fish();
  Cat cat = Cat();
  cat.eat(fish);

  Meat meat = Meat();
  Dog dog = Dog();
  dog.eat(meat);
}

© Habrahabr.ru