В чем разница между абстрактным классом и интерфейсом в Dart?

Ответ

В Dart и абстрактные классы, и интерфейсы используются для определения контрактов, но с разной степенью гибкости и реализации.

Абстрактный класс (abstract class)

  • Может содержать как абстрактные методы (без тела), так и реализованные методы, поля, конструкторы.
  • Поддерживает одиночное наследование (класс может extends только один абстрактный класс).
  • Идеален для создания шаблона с частичной готовой реализацией.

    
    abstract class DataRepository {
    // Поле с реализацией
    final String endpoint = '/api/data';
    
    // Абстрактный метод (контракт)
    Future<List<dynamic>> fetchAll();
    
    // Реализованный метод (общая логика)
    void logError(String error) {
    print('Repository error: $error');
    }
    }

class UserRepository extends DataRepository { @override Future<List> fetchAll() { // Обязаны реализовать только fetchAll() // Метод logError() уже унаследован return HttpClient().get(endpoint); } }


### Интерфейс
*   В Dart **любой класс неявно определяет интерфейс**. Ключевого слова `interface` нет.
*   Для реализации интерфейса используется `implements`.
*   Класс может реализовать **несколько** интерфейсов.
*   При реализации **обязан предоставить свою реализацию ВСЕХ** публичных членов этого класса (и методов, и полей).
```dart
// Классы, которые мы используем как интерфейсы
class JsonSerializable {
  String toJson() => ''; // Сигнатура метода
}

class Identifiable {
  final int id;
  Identifiable(this.id); // Даже поле и конструктор должны быть переопределены
}

// Класс реализует два интерфейса
class User implements JsonSerializable, Identifiable {
  @override
  int id; // Поле должно быть объявлено

  @override
  String toJson() => '{"id": $id}'; // Метод должен быть реализован

  // Конструктор также нужно объявить
  User(this.id);
}

Практическое правило в Dart:

  • Используйте abstract class с extends, когда вам нужна общая базовая логика и отношение "является".
  • Используйте implements, когда вам нужна только сигнатура (контракт) или множественное "наследование" поведения.

Ответ 18+ 🔞

А, ну это классика, блядь! Сидишь такой, пишешь на Dart, и тут бац — нужно решить, какую хуйню использовать: абстрактный класс или интерфейс. А разница-то, ёпта, есть, и нехилая.

Представь себе, что абстрактный класс — это как чертеж от старшего инженера, который уже половину работы за тебя сделал, ебать мои старые костыли. Он тебе и контракт накидает (абстрактные методы), и сразу какую-то готовую хуйню впаяет (реализованные методы, поля). Но есть подвох: наследоваться ты можешь только от одного такого папаши. Один, и всё, пиздец. Как в жизни — отца два не бывает.

abstract class DataRepository {
  // Вот тебе готовое поле, не еби мозги
  final String endpoint = '/api/data';

  // А вот это, сынок, тебе самому делать, контракт, блядь
  Future<List<dynamic>> fetchAll();

  // А это я тебе, ленивая жопа, уже написал, пользуйся
  void logError(String error) {
    print('Repository error: $error');
  }
}

class UserRepository extends DataRepository {
  @override
  Future<List<dynamic>> fetchAll() {
    // Ну вот, только эту херню и реализовать надо
    // logError уже есть, овердохуища удобства!
    return HttpClient().get(endpoint);
  }
}

А теперь смотри сюда, хитрая жопа. Интерфейс в Dart — это вообще пиздопроебибна история. Отдельного слова interface нет, нихуя! Любой класс — он уже неявно интерфейс. И чтобы его реализовать, надо писать implements. И вот тут начинается веселье: ты можешь нацепить на себя интерфейсов, блядь, сколько влезет, как новогоднюю ёлку гирляндами. Но за всё надо платить: ты обязан переписать ВСЁ, что было публичного в том классе. Все методы, все поля — нихуя не унаследуешь, делай сам.

// Смотри, вот два обычных класса. Но для нас они теперь — интерфейсы!
class JsonSerializable {
  String toJson() => ''; // Просто сигнатура, нам она важна
}

class Identifiable {
  final int id;
  Identifiable(this.id); // Ага, и поле, и конструктор — всё часть контракта!
}

// И наш класс хуярит себе два интерфейса сразу
class User implements JsonSerializable, Identifiable {
  @override
  int id; // Поле id? Объявляй сам, сука!

  @override
  String toJson() => '{"id": $id}'; // Метод toJson? Пиши с нуля!

  // И конструктор тоже свой пиши, нихуя не схалявишь
  User(this.id);
}

Так что, чувак, правило простое, как хуй с горы:

  • Тянется extends к abstract class, когда тебе нужен готовый фундамент и ты говоришь "мой класс является таким-то". Логику частично за тебя напишут.
  • Хватаешься implements, когда тебе похуй на реализацию, нужна только форма (контракт), или когда ты хочешь быть одновременно и тем, и этим (множественное "наследование"). Но готовься всё делать сам, доверия ебать ноль.

Выбирай с умом, а то потом сидеть и ебаться с кодом, который не компилится.

Видео-ответы