Почему в Dart 2.12 появился `abstract`?

«Почему в Dart 2.12 появился `abstract`?» — вопрос из категории Dart Core, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Ключевое слово abstract, официально представленное в Dart 2.12 вместе с null safety, не было "новым" в полном смысле. Оно стало обязательным для объявления абстрактных классов. Это изменение усилило выразительность системы типов и сделало дизайн кода более явным и безопасным.

Раньше (до Dart 2.12): Абстрактный класс можно было объявить просто добавив в него абстрактный метод. Это было неявно и могло вводить в заблуждение.

// Старый стиль (неявная абстрактность)
class Animal {
  void makeSound(); // Наличие абстрактного метода делало класс абстрактным
  void sleep() => print('Sleeping');
}
// var a = Animal(); // Runtime error: Cannot instantiate abstract class.

Теперь (Dart 2.12+): Модификатор abstract требуется явно. Это сразу сообщает и разработчику, и инструментам (анализатору, IDE), что класс является контрактом и не может быть инстанциирован.

// Новый стиль (явная абстрактность)
abstract class Animal {
  void makeSound(); // Абстрактный метод
  void sleep() => print('Sleeping'); // Реализованный метод
}

class Dog extends Animal {
  @override
  void makeSound() => print('Woof!');
}

// var a = Animal(); // ОШИБКА КОМПИЛЯЦИИ: Abstract classes can't be instantiated.
var d = Dog(); // OK

Практическая польза в Flutter:

  1. Чёткое разделение ответственности: Позволяет создавать чистые интерфейсы (контракты) для репозиториев, сервисов, use-cases, что является основой для тестируемой архитектуры (например, с Clean Architecture).
  2. Улучшенная поддержка инструментов: IDE может сразу показывать, какие классы нужно реализовать, а какие — нет.
  3. Безопасность: Ошибка попытки создания экземпляра абстрактного класса теперь ловится на этапе компиляции, а не во время выполнения. Это напрямую связано с философией null safety — перемещать ошибки из runtime в compile-time.