0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Dart】Dart 型変性(Variance)

Last updated at Posted at 2025-10-22

1. 型変性とは?

「型変性(variance)」とは、型パラメータの階層関係をどのように扱うかを示す性質です。

つまり、
DogAnimal のサブタイプなら、
List<Dog>List<Animal> のサブタイプになるのか? という話。


2. 基本の型階層(サンプル)

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

(簡単に言えば Dog ⊂ Animal


3. 変性の3種類

変性タイプ 意味 Dartにおける扱い
共変 (Covariant) サブタイプが許可される 戻り値・covariant修飾子
反変 (Contravariant) スーパタイプが許可される メソッド引数(デフォルト)
非変 (Invariant) どちらも許されない ジェネリクス全般 (List<T>)

4. 共変(Covariant)とは?

「サブタイプを代入してもOK」

Animal animal = Dog(); // ✅ OK(共変)

メソッドの戻り値は共変

class Trainer {
  Animal train() => Animal();
}

class DogTrainer extends Trainer {
  @override
  Dog train() => Dog(); // ✅ OK(共変)
}

つまり、サブクラス型を返すのは安全です。


5. 反変(Contravariant)とは?

「スーパタイプを受け取れる(より広い型を受け入れる)」

メソッドの引数は反変

class Trainer {
  void train(Animal a) {}
}

class DogTrainer extends Trainer {
  @override
  void train(Dog d) {} // ❌ NG!引数を狭めてはダメ
}

なぜ?

  • 親クラスの変数で Trainer trainer = DogTrainer(); としたとき、
  • trainer.train(Animal()) を呼べてしまう
  • でも DogTrainer 側は Dog しか受け入れられない → 実行時エラー

→ Dart は安全のため、引数を狭めることを禁止しています。


6. 非変(Invariant)とは?

「型が完全に一致しないと代入できない」

Dart の ジェネリクス(List, Map, Setなど) はすべて非変です。

List<Dog> dogs = [Dog()];

// List<Animal> animals = dogs; // ❌ NG!
List<Animal> animals = dogs.cast<Animal>(); // ✅ 明示キャスト

なぜ非変?

  • 型安全のため
  • もし animals.add(Cat()) できたら、dogsCat が混ざってしまう!

7. covariant 修飾子がもたらす例外

covariant を使うと、引数を共変にできます(=反変ルールを上書き)。

class Trainer {
  void train(covariant Animal a) {}
}

class DogTrainer extends Trainer {
  @override
  void train(Dog d) {} // ✅ OK(covariantによる共変化)
}

でも注意

Trainer trainer = DogTrainer();
trainer.train(Animal()); // ❗ 実行時エラー!

→ コンパイルは通るが、実行時に失敗。
covariant型安全を弱める代償つきの柔軟性です。


8. Dart の変性ルールまとめ(表)

要素 デフォルト変性 共変許可 反変許可 使用例
戻り値 共変 Dogを返すoverride
引数 反変 ❌(通常) Animalを引数に取る
ジェネリクス 非変 List<T>
covariant引数 共変 FlutterのElement.update()

Dartの安全思想:

「ジェネリクスの変性は非変」
「関数の戻り値は共変」
「引数は反変」
「covariantを明示すると例外を作れる」


9. Flutter内部での具体例:Element.update()

abstract class Element {
  void update(covariant Widget newWidget);
}
class StatelessElement extends Element {
  @override
  void update(StatelessWidget newWidget) {
    // 特定Widgetに限定できる!
  }
}

Flutterはこの仕組みで、
StatelessElementStatelessWidget に限定できるようにしている。


まとめ

項目 キーワード Dartでの扱い メモ
共変 covariant 戻り値/covariant修飾子 サブタイプ代入可
反変 contravariant メソッド引数(デフォルト) スーパタイプ代入可
非変 invariant ジェネリクス 一致しないと不可
covariant修飾子 引数型を共変化 実行時エラーリスクあり

Dart の「型変性」は、安全性柔軟性のバランス設計で
「共変=返す方向」「反変=受け取る方向」「非変=固定」と覚えると直感的です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?