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 ジェネリクス(Generics)徹底解説

Posted at

はじめに

Dart の「ジェネリクス(Generics)」は、型安全性を高めながら再利用性の高いコードを書くための仕組みです。
List や Map のような汎用コレクションがまさにその代表例です。


なぜジェネリクスが必要なのか?

型を明示しないと、Dart は dynamic として扱います。
すると、実行時エラーのリスクが増えます。

List items = [1, 2, 3];
items.add("hello"); // ⚠️ 実行時まで気づけない

int first = items[0]; // 実行時に型エラー!

これをジェネリクスで型を固定すれば:

List<int> numbers = [1, 2, 3];
// numbers.add("hello"); ❌ コンパイルエラーで防げる!

メリット

  • 型安全(コンパイル時にエラー検出)
  • コード補完が効く(IDEサポート強化)
  • 再利用性が高い

基本構文

class Box<T> {
  T value;
  Box(this.value);
}

void main() {
  var intBox = Box<int>(123);
  var strBox = Box<String>("Hello");

  print(intBox.value); // 123
  print(strBox.value); // Hello
}

ここでの <T> が「型パラメータ」です。
T は任意の型を受け取る プレースホルダー です。


型パラメータ名の慣習

パラメータ 意味
T Type(一般的な型)
E Element(コレクションの要素)
K Key(Mapのキー)
V Value(Mapの値)

例:

Map<K, V> getDefaultMap<K, V>(K key, V value) {
  return {key: value};
}

型制約(Type Constraints)

ジェネリクスには「上限を設ける(extends制約)」ことも可能です。

class Animal {
  void speak() => print("Animal sound");
}

class Dog extends Animal {
  void speak() => print("Woof!");
}

class Cage<T extends Animal> {
  T animal;
  Cage(this.animal);

  void makeSound() => animal.speak();
}

void main() {
  var dogCage = Cage(Dog());
  dogCage.makeSound(); // Woof!

  // var cage = Cage<String>("hello"); ❌ コンパイルエラー
}

T extends Animal により、TAnimal またはそのサブクラスに限定。


ジェネリクスメソッド

クラスだけでなく、関数にもジェネリクスを使えます。

T getFirst<T>(List<T> items) {
  return items.first;
}

void main() {
  print(getFirst<int>([1, 2, 3])); // 1
  print(getFirst<String>(["a", "b", "c"])); // a
}

複数型パラメータ

class Pair<K, V> {
  final K key;
  final V value;
  Pair(this.key, this.value);
}

void main() {
  var p = Pair<String, int>("age", 20);
  print("${p.key}: ${p.value}");
}

ジェネリクス + ミックスイン / インターフェイス

abstract class Repository<T> {
  void save(T item);
}

class User {
  final String name;
  User(this.name);
}

class UserRepository implements Repository<User> {
  final List<User> _users = [];
  @override
  void save(User user) => _users.add(user);
}

ジェネリクスと covariant / invariant の話

Dart のジェネリクスは 原則 invariant(非変性)
つまり、List<Dog>List<Animal> のサブタイプではありません。

void feedAnimals(List<Animal> animals) {}

void main() {
  List<Dog> dogs = [Dog()];
  // feedAnimals(dogs); ❌ エラー
  feedAnimals(dogs.cast<Animal>()); // ✅ 安全にキャスト
}

実践例:Repositoryパターン

abstract class BaseRepository<T> {
  void insert(T entity);
  T? findById(int id);
}

class User {
  final int id;
  final String name;
  User(this.id, this.name);
}

class UserRepository extends BaseRepository<User> {
  final List<User> _users = [];

  @override
  void insert(User user) => _users.add(user);

  @override
  User? findById(int id) => _users.firstWhere((u) => u.id == id);
}

void main() {
  final repo = UserRepository();
  repo.insert(User(1, "Anna"));
  print(repo.findById(1)?.name); // Anna
}

まとめ

概念 説明
<T> 型パラメータ
extends 型制約
T? / List<T> ジェネリクス活用
関数ジェネリクス T func<T>(...)
型安全 実行前に型ミスを検出できる

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?