クラスに対するextendsとimplements

Dartでは、クラスに対してextendsすることもimplementsすることもできるのが他の言語とは明らかに違うポイントで、書いていて少し混乱したので、整理してみた。

こんな感じの雑なクラスをextendsあるいはimplementsする例を考えてみる。

class Article {
  final String title;
  
  bool get isPublished => _isPublished;
  bool _isPublished = false;

  Article({required this.title});

  void publish() {
    _isPublished = true;
  }
}

extends

extendsはあるクラスを継承したサブクラスを定義する。他の言語と変わったところはなにもない。

class DraftArticle extends Article {
  DraftArticle({required String title}) : super(title: title);
}

スーパークラスを継承しているから、superでスーパークラスを参照できるし、実装もスーパークラスのものをそのまま利用できる。

implements

Dartの面白い仕様の1つに、すべてのクラスが暗黙的にinterfaceを提供するというものがある。implementsはあるクラスが提供するinterfaceを実装する新しいクラスを定義する。

class PublishedArticle implements Article {
  @override
  final String title;
  
  @override
  final bool isPublished = true;
  
  @override
  bool _isPublished = true;
  
  PublishedArticle({required this.title});
  
  @override
  void publish() {}
}

継承する場合と異なる点は以下のようなポイントがある。

  • すべてのメソッドやgetter, setterを実装する必要がある。
  • 複数のスーパークラスを継承できない一方で、複数のクラスのinterfaceであれば実装できる。

この例だと、privateなフィールドまで実装する必要があり、わざわざinterfaceを実装するメリットが感じられない。

abstract class

abstract classと宣言すると、インスタンスを初期化できないクラスを定義できる。また、abstract classは実装を持たないメソッドも定義できる。他の言語に現れるinterfaceに近い。

abstract class Article {
  final String title;

  Article({required this.title});

  void publish();
}

abstract classのサブクラスは実装をもたないメソッドを実装する必要がある。

class DraftArticle extends Article {
  bool get isPublished => _isPublished;
  bool _isPublished = false;
  
  DraftArticle({required String title}) : super(title: title);
  
  @override
  void publish() {
    _isPublished = true;
  }
}