Menu
Coddy logo textTech

ジェネリッククラス

CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 65/110。

Dartの組み込みコレクションである List<String> などが、どのように型パラメータを使用しているかを見てきました。次は、指定した任意の型で動作する独自のジェネリッククラスを作成してみましょう。

ジェネリッククラスは、インスタンスの作成時に提供される実際の型のプレースホルダーとして、型パラメータ(通常は T)を使用します:

class Box<T> {
  T content;
  
  Box(this.content);
  
  T getContent() => content;
}

void main() {
  var stringBox = Box<String>('Hello');
  var intBox = Box<int>(42);
  
  print(stringBox.getContent());  // Hello
  print(intBox.getContent());     // 42
}

T は型変数として機能します。Box<String> を作成すると、クラス内のすべての TString になります。これにより、型ごとに個別のクラスを作成することなく、型安全性が得られます。

クラスが複数の型を扱う必要がある場合、複数の型パラメータを使用できます。

class Pair<K, V> {
  K first;
  V second;
  
  Pair(this.first, this.second);
}

void main() {
  var pair = Pair<String, int>('age', 25);
  print('${pair.first}: ${pair.second}');  // age: 25
}

ジェネリッククラスは、再利用可能で型安全なコードの基盤です。異なる型のためにロジックを重複させたり、dynamic を使用して型情報を失ったりする代わりに、ジェネリクスを使用すると、コンパイラが検証可能な柔軟なコードを記述できます。

challenge icon

チャレンジ

簡単

ジェネリッククラスを使用して、さまざまなタイプのアイテムに対応する柔軟なコンテナを作成するギフトラッピングシステムを構築しましょう。任意のタイプのコンテンツを包むことができるジェネリックな Wrapper クラスと、ギフトとカードのメッセージをペアにするための2つの型パラメータを持つ GiftBox クラスを作成します。

コードを2つのファイルに整理します:

  • wrapper.dart: ここでジェネリッククラスを定義します:
    • T content プロパティとコンストラクタを持つジェネリッククラス Wrapper<T>。コンテンツを返す unwrap() メソッドと、Wrapped: [content] と出力する describe() メソッドを含めます。
    • 2つのプロパティ G giftC card を持つジェネリッククラス GiftBox<G, C>。コンストラクタと、1行目に Gift: [gift]、次の行に Card: [card] と出力する open() メソッドを含めます。
  • main.dart: wrapperファイルをインポートし、異なる型を使用してジェネリッククラスを実演します:
    • 'Chocolate' を含む Wrapper<String> を作成し、describe() を呼び出します。
    • 42 を含む Wrapper<int> を作成し、describe() を呼び出します。
    • 3.14 を含む Wrapper<double> を作成し、describe() を呼び出します。
    • 空行を出力します。
    • ギフトに 'Teddy Bear'、カードに 'Happy Birthday!' を指定して GiftBox<String, String> を作成し、open() を呼び出します。
    • 空行を出力します。
    • ギフトに 100(ギフトカードの金額を表す)、カードに 'Congratulations!' を指定して GiftBox<int, String> を作成し、open() を呼び出します。

同じ Wrapper クラスが、文字列、整数、浮動小数点数に対してシームレスに機能することに注目してください。GiftBox クラスは、2つの型パラメータを使用して異なる型をペアにする方法を示しています。ギフトは任意の型にでき、カードのメッセージは文字列のままにすることができます。

期待される出力:

Wrapped: Chocolate
Wrapped: 42
Wrapped: 3.14

Gift: Teddy Bear
Card: Happy Birthday!

Gift: 100
Card: Congratulations!

チートシート

ジェネリッククラスは、インスタンスの作成時に指定される実際の型のプレースホルダとして、型パラメータ(Tなど)を使用します。

class Box<T> {
  T content;
  
  Box(this.content);
  
  T getContent() => content;
}

void main() {
  var stringBox = Box<String>('Hello');
  var intBox = Box<int>(42);
  
  print(stringBox.getContent());  // Hello
  print(intBox.getContent());     // 42
}

Box<String>を作成すると、クラス内のすべてのTStringになり、型ごとに個別のクラスを作成することなく型安全性が提供されます。

複数の型を扱う場合は、複数の型パラメータを使用します:

class Pair<K, V> {
  K first;
  V second;
  
  Pair(this.first, this.second);
}

void main() {
  var pair = Pair<String, int>('age', 25);
  print('${pair.first}: ${pair.second}');  // age: 25
}

自分で試してみよう

import 'wrapper.dart';

void main() {
  // TODO: 'Chocolate' を含む Wrapper<String> を作成し、describe() を呼び出す
  
  // TODO: 42 を含む Wrapper<int> を作成し、describe() を呼び出す
  
  // TODO: 3.14 を含む Wrapper<double> を作成し、describe() を呼び出す
  
  // TODO: 空行を出力する
  
  // TODO: gift が 'Teddy Bear' の GiftBox<String, String> を作成し、 
  // card に 'Happy Birthday!' を指定して open() を呼び出す
  
  // TODO: 空行を出力する
  
  // TODO: gift が 100 の GiftBox<int, String> を作成し、 
  // card に 'Congratulations!' を指定して open() を呼び出す
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン