🎁

【Dart】abstract,mixin,extends,implements,with等の使い方基礎

2020/12/16に公開

【Dart】クラスを利用したクラス作成の基本例

事前知識

  • Upcastとは

    • サブクラスのオブジェクトをスーパークラスと同じように扱うこと
      • =サブクラスのオブジェクトをスーパークラスのオブジェクトに対応できるように変換
    • 自動で行われる
  • Downcastとは

    • スーパークラスのオブジェクトをサブクラスと同じように扱うこと
      • =スーパークラスのオブジェクトをサブクラスのオブジェクトに対応できるように変換
    • 自動で行われない
  • クラスを利用したクラス作成

方法 記述法 複数指定 関数のオーバライド
通常の継承(extends) class B extends A {} 適宜必要な関数のみ
abstractを利用した継承(extends) class B extends A {}※Aは基本的にabstractクラス 全関数必須
Interfaceとは(implements) class B implements A {} 全関数必須
Mixins(with) class B with A {}※Aは基本的にmixinクラス 適宜必要な関数のみ
  • 上記以外の理解
    • 通常の継承(extends)

      • 一番オーソドックスなトップダウン的考え方
      • 複数指定不可 && オーバーライドしなくてよい = スーパークラス
    • abstractを利用した継承(extends)

      • extendsを利用し、他のサブクラスのスーパークラスとする目的のクラス

      • 全関数オーバーライド必須=クラスが必ずその関数を持つことを保証

      • 共通で持つ変数を一括定義

      • 複数指定不可 && オーバーライド必須 = 他クラス利用前提スーパークラス(変数と処理実装または未実装の関数置き場)

    • Interface(implements)

      • 全関数オーバーライド必須=クラスが必ずその関数を持つことを保証
      • 基本的にはインターフェイス(関数定義)を利用するためのもの
      • 複数指定可能 && オーバーライド必須 = 処理未実装の関数定義置き場
    • Mixins(with)

      • 階層構造的な継承ではなく、追加で外付けするイメージ
      • 注意点として、コンストラクタは持たない
      • 複数指定可能 && オーバーライドしなくてよい = 処理を実装した関数置き場

UpcastとDowncast

  • オブジェクト作成例
    • obj1:SuperClassオブジェクトをSuperClassとして扱う
    • obj2:SubClassオブジェクトをSubClassとして扱う
    • obj3:SubClassオブジェクトをSuperClassとして扱う(Upcast)
    • obj4:SuperClassオブジェクトをSubClassとして扱う(Downcast)
      • ※エラーとなる
class SuperClass {
  void test() => print('SuperClass');
}

class SubClass extends SuperClass {
  
  void test() => print('SubClass');
}

void main() {
  // 通常利用
  SuperClass obj1 = SuperClass();
  obj1.test();
  SubClass obj2 = SubClass();
  obj2.test();
  // Upcast
  SuperClass obj3 = SubClass();
  obj3.test();
  // Downcast
  SubClass obj4 = obj1;
  obj4.test();
}

実行結果

SuperClass
SubClass
SubClass
Uncaught Error: TypeError: Instance of 'SuperClass': type 'SuperClass' is not a subtype of type 'SubClass'

スーパークラスの関数を呼び出す(super.[関数];

  • スーパークラスで定義された関数を利用
    • super.[関数];
class SuperClass {
  void test() => print('SuperClass');
}

class SubClass extends SuperClass {
  
  void test() {
    super.test();
    print('SubClass');
  }
}

void main() {
  SubClass obj = SubClass();
  obj.test();
}

実行結果

SuperClass
SubClass

オーバーライドする関数の引数の型を変更(covariant)

  • 目的

    • 継承関係にあるものを引数とする場合に、
    • オーバーライドする関数の引数の型を元関数の型より狭めたい場合
  • 方法

    • covariant を引数の型の前に追加することで、型を変更したオーバーライドが可能
      • ※引数を無しにすることは不可
  • 失敗例

    • SubClassのvoid test(SubClassForArgument obj)で以下エラー
      • 'SubClass.test' ('void Function(SubClassForArgument)') isn't a valid override of 'SuperClass.test' ('void Function(SuperClassForArgument)')
        • SuperClassでvoid test(SuperClassForArgument obj)となっているため、SubClassForArgumentを引数としてしまうとDowncastを誘発してしまう可能性がある
class SuperClassForArgument {}

class SubClassForArgument extends SuperClassForArgument {}

class SuperClass {
  void test(SuperClassForArgument obj) => print('SuperClass:' + obj.toString());
}

class SubClass extends SuperClass {
  
  void test(SubClassForArgument obj) {
    print('SubClass:' + obj.toString());
  }
}

void main() {
  SubClass obj = SubClass();
  obj.test(SubClassForArgument());
}
  • 成功例①
    • covariantをSubClassの関数の引数に追加するとエラーが無くなる
class SuperClassForArgument {}

class SubClassForArgument extends SuperClassForArgument {}

class SuperClass {
  void test(SuperClassForArgument obj) => print('SuperClass:' + obj.toString());
}

class SubClass extends SuperClass {
  
  void test(covariant SubClassForArgument obj) {
    print('SubClass:' + obj.toString());
  }
}

void main() {
  SubClass obj = SubClass();
  obj.test(SubClassForArgument());
}

実行結果

SubClass:Instance of 'SubClassForArgument'
  • 成功例②
    • covariantをSubClassの関数の引数ではなく、SuperClassの関数の引数に追加した場合もエラーが無くなる
      • こちらの方が、SubClassに追加する必要が無いので一括設定可能
class SuperClassForArgument {}

class SubClassForArgument extends SuperClassForArgument {}

class SuperClass {
  void test(covariant SuperClassForArgument obj) => print('SuperClass:' + obj.toString());
}

class SubClass extends SuperClass {
  
  void test(SubClassForArgument obj) {
    print('SubClass:' + obj.toString());
  }
}

void main() {
  SubClass obj = SubClass();
  obj.test(SubClassForArgument());
}

実行結果

SubClass:Instance of 'SubClassForArgument'

引数が継承関係にあるものの場合のみで、stringを引数無しや、intにしようとしてもエラー

  • 【備考】失敗例:引数が継承関係にあるものの場合のみ対象となる
    (stringを、intにしようとしてもエラー)
    • SubClassで引数をintにしたいと思った場合
      • SubClassのvoid test(int i)で以下エラー
        • 'SubClass.test' ('void Function(int)') isn't a valid override of 'SuperClass.test' ('void Function(String)')
      • SubClassのprint('SubClass:'+i);で以下エラー
        • The argument type 'int' can't be assigned to the parameter type 'String'
class SuperClass {
  void test(covariant String s) => print('SuperClass:'+s);
}

class SubClass extends SuperClass {
  
  void test(int i) {
    print('SubClass:'+i);
  }
}

void main() {
  SubClass obj = SubClass();
  obj.test(1);
}

以下、コンストラクタ関係

superコンストラクタ

  • superコンストラクタを利用することで、スーパークラスのコンストラクタを呼ぶ
    • SubClass(this.b) : super(b);とすることで、SuperClass(this.a);も呼ばれる
      • =SubClassのオブジェクトは、変数aもbも持つ
    • SubClass(this.b);のみだと以下エラー
      • The superclass 'SuperClass' doesn't have a zero argument constructor
class SuperClass {
  String a;
  SuperClass(this.a);
  void test() => print('SuperClass: $a');
}

class SubClass extends SuperClass {
  String b;
  SubClass(this.b) : super(b);
  
  void test() => print('SubClass: $b');
}

void main() {
  SubClass obj = SubClass('Hello');
  obj.test();
  print(obj.a);
  print(obj.b);
}

実行結果

SubClass: Hello
Hello
Hello
  • 上記でSubClass(this.b);のみだと以下エラーであるように、SuperClassの引数を持つコンストラクタを削除すると、エラーはなくなる=SuperClass();が呼ばれるのみなので、aはnull
    • The superclass 'SuperClass' doesn't have a zero argument constructor
class SuperClass {
  String a;
  void test() => print('SuperClass: $a');
}

class SubClass extends SuperClass {
  String b;
  SubClass(this.b);
  
  void test() => print('SubClass: $b');
}

void main() {
  SubClass obj = SubClass('Hello');
  obj.test();
  print(obj.a);
  print(obj.b);
}

実行結果

SubClass: Hello
null
Hello
  • 【備考】上記と同様の意味のコード
class SuperClass {
  String a;
  SuperClass();
  void test() => print('SuperClass: $a');
}

class SubClass extends SuperClass {
  String b;
  SubClass(this.b) : super();
  
  void test() => print('SubClass: $b');
}

void main() {
  SubClass obj = SubClass('Hello');
  obj.test();
  print(obj.a);
  print(obj.b);
}

実行結果

SubClass: Hello
null
Hello
  • 実際にsuperコンストラクタ利用する際は、基本はsuperには何か値を渡す時にのみ呼ぶべき
class SuperClass {
  String a;
  SuperClass(this.a);
  void test() => print('SuperClass: $a');
}

class SubClass extends SuperClass {
  String b;
  SubClass(this.b) : super(b*2);
  
  void test() => print('SubClass: $b');
}

void main() {
  SubClass obj = SubClass('Hello');
  obj.test();
  print(obj.a);
  print(obj.b);
}

実行結果

SubClass: Hello
HelloHello
Hello

abstractクラス(extends)

  • 他のクラスでクラスそのものを利用するためのabstractクラス
    • abstractクラスでは、void test();のように空の関数を置いておける
      • @overrideは省略可能だが、非推奨
    • abstractが付くクラスは、それ自体をオブジェクト化しようとするとエラーとなる
      • AbstractSuperClass obj = AbstractSuperClass();とすると以下エラー
        • Abstract classes can't be instantiated
abstract class AbstractSuperClass {
  void test();
}

class SubClass extends AbstractSuperClass {
  
  void test() {
    print('SubClass');
  }
}

void main() {
  SubClass obj = SubClass();
  obj.test();
}

実行結果

SubClass
  • abstractを付けない以下の場合も同様なことが可能だが、
    空関数についてoverride必須ではないので、あまり意味がない
    • 特定の関数を必ず持っているというルールを作れない
    • 未実装の場合にエラーで教えてくれることが無い
class SuperClass {
  void test() {}
}

class SubClass extends SuperClass {
  
  void test() {
    print('SubClass');
  }
}

void main() {
  SubClass obj = SubClass();
  obj.test();
}

実行結果

SubClass
  • 変数やコンストラクタを持つ場合
    • ※通常と同様
abstract class AbstractSuperClass {
  String a;
  AbstractSuperClass(this.a);
  void test();
}

class SubClass extends AbstractSuperClass {
  String b;
  SubClass(this.b) : super(b*2);
  
  void test() => print('SubClass: $b');
}

void main() {
  SubClass obj = SubClass('Hello');
  obj.test();
  print(obj.a);
  print(obj.b);
}

実行結果

SubClass: Hello
HelloHello
Hello
  • GetterとSetterの空の関数をabstractクラスで定義する例
abstract class Creature {
  int _number;
  String _name;
  Creature(this._number, this._name);

  // Abstract Getter
  int get number;
  String get name;

  // Abstract Setter
  set name(String s);
}

class Monster extends Creature {
  Monster(number, name) : super(number, name);

  // Getter
  
  int get number => _number;
  
  String get name => _name;

  // Setter
  
  set name(String s) {
    if (s.length > 0 && s.length < 11) {
      _name = s;
    } else {
      print('$s:文字数を1文字以上10文字以下にしてください。');
    }
  }
}

void main() {
  final monster = Monster(1, 'Slime');
  print(monster.number.toString() + ':' + monster.name);
  monster.name = 'Slaline';
  print(monster.number.toString() + ':' + monster.name);
}

実行結果

1:Slime
1:Slaline

Interface(implements)

  • 他のクラスで定義したインターフェイス(関数定義)を利用するためクラス
    • abstractクラスをextendsする際と同様
      • 空関数もoverride必須
abstract class AbstractInterfaceClass {
  void test();
}

class TestClass implements AbstractInterfaceClass {
  
  void test() {
    print('TestClass');
  }
}

void main() {
  TestClass obj = TestClass();
  obj.test();
}

実行結果

TestClass
  • 複数implements可能
abstract class AbstractInterfaceClass1 {
  void test1();
}

abstract class AbstractInterfaceClass2 {
  void test2();
}

class TestClass implements AbstractInterfaceClass1, AbstractInterfaceClass2 {
  
  void test1() {
    print('TestClass:test1');
  }

  
  void test2() {
    print('TestClass:test2');
  }
}

void main() {
  TestClass obj = TestClass();
  obj.test1();
  obj.test2();
}

実行結果

TestClass:test1
TestClass:test2
  • abstractをつけないクラスを利用しても、上記と同様結果
    • 通常は、空関数の記載文字量が少ないabstractクラスを利用
class InterfaceClass1 {
  void test1() {}
}

class InterfaceClass2 {
  void test2() {}
}

class TestClass implements InterfaceClass1, InterfaceClass2 {
  
  void test1() {
    print('TestClass:test1');
  }

  
  void test2() {
    print('TestClass:test2');
  }
}

void main() {
  TestClass obj = TestClass();
  obj.test1();
  obj.test2();
}

実行結果

TestClass:test1
TestClass:test2
  • implementsの場合は、abstractがついていないクラスをimplementsした時も、空関数のoverride必須
    • test1をoverrideしないとclass TestClassの行で以下エラー
      • Missing concrete implementation of 'InterfaceClass1.test1'
class InterfaceClass1 {
  void test1() {}
}

class InterfaceClass2 {
  void test2() {}
}

class TestClass implements InterfaceClass1, InterfaceClass2 {
//   @override
//   void test1() {
//     print('TestClass:test1');
//   }

  
  void test2() {
    print('TestClass:test2');
  }
}

void main() {
  TestClass obj = TestClass();
  obj.test1();
  obj.test2();
}
  • 変数やコンストラクタを持つ場合
    • implementsすることは可能(変数やコンストラクタを上手く利用できない)
    • ※通常と異なる
      • superコンストラクタの引数付きが呼べない
        • SubClass(this.a) : super();は可能
      • どんな関数を持つかのみInterfaceにするクラスに定義すべき
abstract class AbstractInterfaceClass {
  String a;
  AbstractInterfaceClass(this.a);
  void test();
}

class TestClass implements AbstractInterfaceClass {
  String a;
  TestClass(this.a);
  
  void test() => print('TestClass: $a');
}

void main() {
  TestClass obj = TestClass('Hello');
  obj.test();
  print(obj.a);
}

実行結果

TestClass: Hello
Hello
  • 【備考】上記と同様
abstract class AbstractInterfaceClass {
//   String a;
//   AbstractInterfaceClass(this.a);
  void test();
}

class TestClass implements AbstractInterfaceClass {
  String a;
  TestClass(this.a) : super();
  
  void test() => print('TestClass: $a');
}

void main() {
  TestClass obj = TestClass('Hello');
  obj.test();
  print(obj.a);
}

実行結果

TestClass: Hello
Hello

extendsとimplementsの利用例

  • extendsは一つのみだが、その後ろにimplementsを複数繋げられる
// For extends
abstract class AbstractSuperClass {
  void test1();
}

// For implements
abstract class AbstractInterfaceClass1 {
  void test2();
}

abstract class AbstractInterfaceClass2 {
  void test3();
}

class SubClass extends AbstractSuperClass
    implements AbstractInterfaceClass1, AbstractInterfaceClass2 {
  
  void test1() {
    print('SubClass:test1');
  }

  
  void test2() {
    print('SubClass:test2');
  }

  
  void test3() {
    print('SubClass:test3');
  }
}

void main() {
  SubClass obj = SubClass();
  obj.test1();
  obj.test2();
  obj.test3();
}

実行結果

SubClass:test1
SubClass:test2
SubClass:test3
  • 複数のサブクラスが持つべき、処理が共通の関数がある場合はextendsが便利
    • 処理が共通の関数はそのまま中身も実装
    • サブクラスによって動作が変わるものは、空関数としておけばいい
// For extends
abstract class Creature {
  String name;
  int hp;
  Creature(this.name, this.hp);

  bool isDead() => hp <= 0;
  void attack();
}

class Human extends Creature {
  Human(name, hp) : super(name, hp);

  
  void attack() {
    if (isDead()) {
      print('瀕死');
      return;
    }
    print('パンチ');
  }
}

class Monster extends Creature {
  Monster(name, hp) : super(name, hp);

  
  void attack() {
    if (isDead()) {
      print('瀕死');
      return;
    }
    print('魔法');
  }
}

void main() {
  final human = Human('Warrior', 10);
  final monster1 = Monster('Slime', 0);
  final monster2 = Monster('Dragon', 100);
  human.attack();
  monster1.attack();
  monster2.attack();
}

実行結果

パンチ
瀕死
魔法
  • 複数のサブクラスが持つべき、処理が共通の関数が無い場合はimplementsが便利
    • 空関数のみを定義しておく
      • final i = 100;等は利用可能
// For implements
abstract class Item {
  void useItem();
}

// For extends
abstract class Creature {
  String name;
  int hp;
  Creature(this.name, this.hp);

  bool isDead() => hp <= 0;
  void attack();
}

class Human extends Creature implements Item {
  Human(name, hp) : super(name, hp);

  
  void attack() {
    if (isDead()) {
      print('瀕死');
      return;
    }
    print('パンチ');
  }

  
  void useItem() {
    print('剣を使う');
  }
}

class Monster extends Creature implements Item {
  Monster(name, hp) : super(name, hp);

  
  void attack() {
    if (isDead()) {
      print('瀕死');
      return;
    }
    print('魔法');
  }

  
  void useItem() {
    print('魔石を使う');
  }
}

void main() {
  final human = Human('Warrior', 10);
  final monster = Monster('Slime', 0);
  human.useItem();
  monster.useItem();
}

実行結果

剣を使う
魔石を使う
  • 同じ系統のものに利用する場合に、implementsを利用
    • 以下のようにしてクラスを作成することも可能
      (クラスとして作成しておきたい場合に有用?)
abstract class Item {
  String name();
  int usageCost();
  void useItem();
}

class FlameWand implements Item {
  
  String name() => 'Flame Wand';

  
  int usageCost() => 5;

  
  void useItem() => print('Fire!');
}

class LightningSword implements Item {
  
  String name() => 'Lightning Sword';

  
  int usageCost() => 10;

  
  void useItem() => print('Thunder!');
}

void main() {
  final wand = FlameWand();
  final sword = LightningSword();
  print(wand.name() + ':' + wand.usageCost().toString());
  wand.useItem();

  print(sword.name() + ':' + sword.usageCost().toString());
  sword.useItem();
}

実行結果

Flame Wand:5
Fire!
Lightning Sword:10
Thunder!
  • 同じ系統のものに利用する場合に、extendsとimplementsの併用
    • 共通の関数や状態を管理する変数のためには、extends用のクラスを作成
    • 適宜追加する関数の定義として、implements用のクラスを作成
// For extends
abstract class Weapon {
  String name;
  Weapon(this.name);
}

// For implements
abstract class Fire {
  void fire();
}

abstract class Thunder {
  void thunder();
}

class LightningFlameSword extends Weapon implements Fire, Thunder{
  LightningFlameSword(name):super(name);
  
  void fire() => print('Fire! :5 damage');

  
  void thunder() => print('Thunder! :10 damage');
}

void main() {
  final weapon = LightningFlameSword('雷炎剣');
  weapon.fire();
  weapon.thunder();
}

実行結果

Fire! :5 damage
Thunder! :10 damage

Mixin(with)

  • ある範囲の必要な関数をまとめて追加(オーバーライド不要なので、楽)
    • withを利用
class Work {
  void telework() => print('テレワーク');
  void workplaceWork() => print('職場ワーク');
}

class Human with Work {}

void main() {
  final human = Human();
  human.telework();
  human.workplaceWork();
}

実行結果

テレワーク
職場ワーク
  • 「class」では「mixin」に変更することで、mixin用途であることを示し、間違い予防
    • 誤ってコンストラクタ(Work();)を付けると以下エラー
      • Mixins can't declare constructors
    • オーバライドも可能
mixin Work {
  void telework() => print('テレワーク');
  void workplaceWork() => print('職場ワーク');
}

class Human with Work {}

void main() {
  final human = Human();
  human.telework();
  human.workplaceWork();
}

実行結果

テレワーク
職場ワーク
  • 複数指定可能
mixin Work {
  void telework() => print('テレワーク');
  void workplaceWork() => print('職場ワーク');
}

mixin Refresh {
  void game() => print('ゲーム');
  void picnic() => print('ピクニック');
}

class Human with Work, Refresh {}

void main() {
  final human = Human();
  human.telework();
  human.workplaceWork();
  human.game();
  human.picnic();
}

実行結果

テレワーク
職場ワーク
ゲーム
ピクニック
  • withを利用しているクラスをextendsし、さらにwithを利用
mixin Work {
  void telework() => print('テレワーク');
  void workplaceWork() => print('職場ワーク');
}

mixin Refresh {
  void game() => print('ゲーム');
  void picnic() => print('ピクニック');
}

mixin Investment {
  void stock() => print('株');
  void cryptocurrency() => print('仮想通貨');
}

class Human with Work, Refresh {}

class Investor extends Human with Investment{}

void main() {
  final human = Investor();
  human.telework();
  human.workplaceWork();
  human.game();
  human.picnic();
  human.stock();
  human.cryptocurrency();
}

実行結果

テレワーク
職場ワーク
ゲーム
ピクニック
株
仮想通貨
  • 「on」で制限:mixin [作成クラス名] on [制限するスーパークラス名]
    • onで指定したクラスをスーパークラスとした場合のみwithで利用可能
      • mixin Investment on Human{}とした場合はInvestmentのwith利用は以下
        • class [クラス名] extends Human with Investment {}
mixin Work {
  void telework() => print('テレワーク');
  void workplaceWork() => print('職場ワーク');
}

mixin Refresh {
  void game() => print('ゲーム');
  void picnic() => print('ピクニック');
}

mixin Investment on Human {
  void stock() => print('株');
  void cryptocurrency() => print('仮想通貨');
}

class Human with Work, Refresh {}

class Investor extends Human with Investment {}

void main() {
  final human = Investor();
  human.telework();
  human.workplaceWork();
  human.game();
  human.picnic();
  human.stock();
  human.cryptocurrency();
}

実行結果

テレワーク
職場ワーク
ゲーム
ピクニック
株
仮想通貨
  • コード記載後に、mixinを利用した方が便利になる例
    • 何かの制約で、スーパークラスに関数を追加出来ない場合
      • mixinがコードの重複を避ける手助けになる可能性がある
    • 先ほどの前章で利用した例を書き換え
abstract class Creature {
  String name;
  int hp;
  Creature(this.name, this.hp);

  bool isDead() => hp <= 0;
  void attack();
}

class Human extends Creature {
  Human(name, hp) : super(name, hp);

  
  void attack() {
    if(isDead()){
      print('瀕死');
      return;
    }
    print('パンチ');    
  }
}

class Monster extends Creature {
  Monster(name, hp) : super(name, hp);

  
  void attack() {
    if(isDead()){
      print('瀕死');
      return;
    }
    print('魔法');    
  }
}

void main() {
  final human = Human('Warrior', 10);
  final monster1 = Monster('Slime', 0);
  final monster2 = Monster('Dragon', 100);
  human.attack();
  monster1.attack();
  monster2.attack();
}

→【修正後以下】

mixin Attack {
  void attack(bool isDead, String attackName) {
    if (isDead) {
      print('瀕死');
      return;
    }
    print(attackName);
  }
}

abstract class Creature {
  String name;
  int hp;
  Creature(this.name, this.hp);

  bool isDead() => hp <= 0;
}

class Human extends Creature with Attack {
  Human(name, hp) : super(name, hp);
}

class Monster extends Creature with Attack {
  Monster(name, hp) : super(name, hp);
}

void main() {
  final human = Human('Warrior', 10);
  final monster1 = Monster('Slime', 0);
  final monster2 = Monster('Dragon', 100);
  human.attack(human.isDead(), 'パンチ');
  monster1.attack(monster1.isDead(), '魔法');
  monster2.attack(monster2.isDead(), '魔法');
}

実行結果

パンチ
瀕死
魔法

factoryとabstract

  • factoryでreturnするデフォルトのクラスを決めることが出来る

    • =実際には別のクラスを呼べる
  • 本来はabstractクラスではオブジェクトが作成出来ないが、この場合可能

    • 実際はfactoryを利用し、etends先のSubClassのコンストラクタを呼んでいる
      (=SubClassオブジェクトが作成される)
      • ※コンストラクタ同士の引数が同じである必要がある
abstract class AbstractSuperClass {
  AbstractSuperClass();
  void test();
  factory AbstractSuperClass.testFactory() {
    return SubClass();
  }
}

class SubClass extends AbstractSuperClass {
  
  void test() {
    print('SubClass');
  }
}

void main() {
  AbstractSuperClass obj = AbstractSuperClass.testFactory();
  print(obj);
  obj.test();
}

実行結果

Instance of 'SubClass'
SubClass
  • 利用例
    • abstractクラス
      • 処理が共通の関数はabstractクラスに中身を記載(common method)
      • 処理が共通ではないが、必要な関数は空関数として定義(not common method)
    • メリット
      • Userクラスをextendsしたクラスや、それを利用した変数が大量にあり、
        デフォルトとなるクラスがある場合に一括設定可能
        • 例:デフォルトをCommonクラスに変更する場合は、factoryを以下修正
          • return AnonymousUser(id);return CommonUser(id);
abstract class User {
  String id;

  User(this.id);
  factory User.defaultUser(id) {
    return AnonymousUser(id);
  }

  // common method
  void changeID(String newId) {
    id = newId;
  }

  // not common method
  bool authentication();
}

class AnonymousUser extends User {
  AnonymousUser(id) : super(id);

  
  bool authentication() => false;
}

class CommonUser extends User {
  CommonUser(id) : super(id);

  
  bool authentication() => true;
}

void main() {
  final user1 = User.defaultUser('TestUser1');
  print(user1);
  print(user1.id);
  print(user1.authentication());

  final user2 = CommonUser('TestUser2');
  print(user2);
  print(user2.id);
  print(user2.authentication());
}

実行結果

Instance of 'AnonymousUser'
TestUser1
false
Instance of 'CommonUser'
TestUser2
true

参考

Discussion