Menu
Coddy logo textTech

Ключевое слово covariant

Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 61 из 110.

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

Рассмотрим сценарий, в котором класс Animal имеет метод, который принимает другое Animal:

class Animal {
  void chase(Animal other) {
    print('Chasing an animal');
  }
}

class Dog extends Animal {
  @override
  void chase(covariant Dog other) {
    print('Dog chasing another dog');
  }
}

Без covariant Dart выдал бы ошибку, что Dog не является тем же типом, что и Animal. Добавив covariant перед параметром, вы сообщаете Dart: «Я знаю, что это более специфичный тип, и я гарантирую, что будет передан только правильный тип».

Это полезно, когда подклассы по логике должны работать только с объектами своего типа:

void main() {
  Dog dog1 = Dog();
  Dog dog2 = Dog();
  
  dog1.chase(dog2);  // Работает: Собака преследует другую собаку
}

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

challenge icon

Задание

Легко

Давайте создадим систему совместимости домашних животных, которая использует ключевое слово covariant, чтобы гарантировать, что животные могут играть только с представителями своего вида. Вы создадите иерархию домашних животных, где каждый тип имеет метод playWith, принимающий только тот же тип животного.

Вы организуете свой код в два файла:

  • pets.dart: Определите здесь вашу иерархию животных:
    • Класс Pet со свойством String name и конструктором. Включите метод playWith(Pet other), который выводит [name] plays with [other.name]
    • Класс Cat, который расширяет Pet и переопределяет playWith, используя ключевое слово covariant, чтобы принимать только Cat. Он должен выводить [name] and [other.name] chase yarn together
    • Класс Dog, который расширяет Pet и переопределяет playWith, используя ключевое слово covariant, чтобы принимать только Dog. Он должен выводить [name] and [other.name] fetch the ball together
    • Класс Rabbit, который расширяет Pet и переопределяет playWith, используя ключевое слово covariant, чтобы принимать только Rabbit. Он должен выводить [name] and [other.name] hop around together
  • main.dart: Импортируйте файл с животными и продемонстрируйте, как каждый тип животных играет исключительно со своим видом:
    • Создайте два объекта Cat с именами 'Whiskers' и 'Mittens', затем заставьте Whiskers играть с Mittens
    • Создайте два объекта Dog с именами 'Buddy' и 'Max', затем заставьте Buddy играть с Max
    • Создайте два объекта Rabbit с именами 'Flopsy' и 'Cotton', затем заставьте Flopsy играть с Cotton

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

Ожидаемый вывод:

Whiskers and Mittens chase yarn together
Buddy and Max fetch the ball together
Flopsy and Cotton hop around together

Шпаргалка

Ключевое слово covariant позволяет подклассу переопределять метод с более специфичным типом параметра, чем у родительского класса.

Без covariant Dart требует, чтобы типы параметров точно совпадали при переопределении методов. С covariant вы можете сузить тип параметра в подклассе:

class Animal {
  void chase(Animal other) {
    print('Chasing an animal');
  }
}

class Dog extends Animal {
  @override
  void chase(covariant Dog other) {
    print('Dog chasing another dog');
  }
}

Это полезно, когда подклассы должны работать только со своим собственным типом:

void main() {
  Dog dog1 = Dog();
  Dog dog2 = Dog();
  
  dog1.chase(dog2);  // Works: Dog chasing another dog
}

Важно: Использование covariant переносит проверку типов со времени компиляции на время выполнения. Если вы вызовете метод через ссылку родительского класса с неправильным типом, вы получите ошибку времени выполнения вместо ошибки компиляции.

Попробуйте сами

import 'pets.dart';

void main() {
  // TODO: Создайте два объекта Cat с именами 'Whiskers' и 'Mittens'
  // Затем заставьте Whiskers поиграть с Mittens
  
  
  // TODO: Создайте два объекта Dog с именами 'Buddy' и 'Max'
  // Затем заставьте Buddy поиграть с Max
  
  
  // TODO: Создайте два объекта Rabbit с именами 'Flopsy' и 'Cotton'
  // Затем заставьте Flopsy поиграть с Cotton
  
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование