ジェネリッククラス
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> を作成すると、クラス内のすべての T が String になります。これにより、型ごとに個別のクラスを作成することなく、型安全性が得られます。
クラスが複数の型を扱う必要がある場合、複数の型パラメータを使用できます。
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 を使用して型情報を失ったりする代わりに、ジェネリクスを使用すると、コンパイラが検証可能な柔軟なコードを記述できます。
チャレンジ
簡単ジェネリッククラスを使用して、さまざまなタイプのアイテムに対応する柔軟なコンテナを作成するギフトラッピングシステムを構築しましょう。任意のタイプのコンテンツを包むことができるジェネリックな Wrapper クラスと、ギフトとカードのメッセージをペアにするための2つの型パラメータを持つ GiftBox クラスを作成します。
コードを2つのファイルに整理します:
wrapper.dart: ここでジェネリッククラスを定義します:T contentプロパティとコンストラクタを持つジェネリッククラスWrapper<T>。コンテンツを返すunwrap()メソッドと、Wrapped: [content]と出力するdescribe()メソッドを含めます。- 2つのプロパティ
G giftとC 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>を作成すると、クラス内のすべてのTがStringになり、型ごとに個別のクラスを作成することなく型安全性が提供されます。
複数の型を扱う場合は、複数の型パラメータを使用します:
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() を呼び出す
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4Null Safety
Null Safety 入門Nullable と Non-Nullable? 演算子と ! 演算子late キーワードと Null SafetyNull-Aware 演算子クラスにおける Null Safetyまとめ:ユーザープロフィールシステム10コレクションとジェネリクス
List, Set, Map の概要型安全なコレクションジェネリッククラスジェネリックメソッドジェネリクスの制約Iterable と Iteratorまとめ:ジェネリックなストレージ