Dartは純粋なオブジェクト指向プログラミング言語であり、すべてのデータ型はクラスを基盤としています。
Dartのクラス設計には、コードのボイラープレート(定型文)を大幅に削減するための強力な仕組みが揃っています。
例えば、インスタンス変数の自動初期化糖衣構文(this.field)、名前付きコンストラクタ、ライブラリ単位のカプセル化、そしてインスタンス生成ロジックを柔軟に制御できる factory コンストラクタなどです。
この章では、Dartにおけるクラスの基礎から実践的なオブジェクトモデリングまでを徹底解説します。
newの省略)Dartでオブジェクトの設計図となる クラス(class) を定義し、インスタンスを生成する基本的な構文を見てみましょう。
Dartでは、引数をそのままフィールドに代入する場合、Point(this.x, this.y); のように宣言するだけで初期化処理が完了します。また、インスタンス生成時の new は省略するのが標準です。
class Point {
// フィールド
final double x;
final double y;
// コンストラクタ(糖衣構文 this.x, this.y)
Point(this.x, this.y);
// メソッド
void printCoordinates() {
print('Point($x, $y)');
}
}
void main() {
// new キーワードは省略可能
final p1 = Point(3.0, 4.0);
p1.printCoordinates();
}dart run point_class.dartPoint(3.0, 4.0)
Dartでは、1つのクラスに複数の異なる初期化方法を提供するために 名前付きコンストラクタ や リダイレクトコンストラクタ を作成できます。
ClassName.identifier(...) の形式で定義。: this(...) で別コンストラクタに初期化を委譲。const コンストラクタ: 全フィールドが final の場合、コンパイル時定数としてインスタンス化可能。class User {
final String name;
final int age;
// 基本コンストラクタ (const対応)
const User(this.name, this.age);
// 名前付きコンストラクタ
User.guest()
: name = 'ゲスト',
age = 0;
// リダイレクトコンストラクタ
User.adult(String name) : this(name, 20);
void display() {
print('ユーザー: $name (年齢: $age)');
}
}
void main() {
const u1 = User('Alice', 25);
final u2 = User.guest();
final u3 = User.adult('Bob');
u1.display();
u2.display();
u3.display();
}dart run constructors_demo.dartユーザー: Alice (年齢: 25) ユーザー: ゲスト (年齢: 0) ユーザー: Bob (年齢: 20)
:)と super の呼び出しコンストラクタ実行前にフィールドの計算やバリデーションを行う 初期化子リスト と、親クラスへの引数委譲について学びます。
初期化子リスト(コロン : に続く記述)では、フィールドの事前計算や assert による引数検証が可能です。また、親クラスへの引数転送には super.field が使えます。
class Shape {
final String type;
Shape(this.type);
}
class Rectangle extends Shape {
final double width;
final double height;
final double area;
// 初期化子リストで面積計算、バリデーション、親クラス呼出
Rectangle(this.width, this.height)
: area = width * height,
assert(width > 0, 'width は正の数である必要があります'),
super('Rectangle');
void info() => print('図形: $type, 幅: $width, 高さ: $height, 面積: $area');
}
void main() {
final rect = Rectangle(5.0, 8.0);
rect.info();
}dart run super_params.dart図形: Rectangle, 幅: 5.0, 高さ: 8.0, 面積: 40.0
_ によるプライベート化と get / set)Dartには public、private などのアクセス修飾子キーワードがありません。
識別子の先頭に アンダースコア _ を付けることで、その要素は ライブラリ(同一ファイル)プライベート になります。
非公開フィールドに対して安全な読み書き手段を提供するために get と set を定義します。
class BankAccount {
// プライベートフィールド
double _balance = 0.0;
// ゲッター
double get balance => _balance;
// セッター
set balance(double value) {
if (value < 0) {
print('エラー: 残高を負の値にすることはできません');
return;
}
_balance = value;
}
void deposit(double amount) {
if (amount > 0) _balance += amount;
}
}
void main() {
final account = BankAccount();
account.deposit(5000);
print('残高: ${account.balance} 円');
account.balance = 8000;
print('更新後残高: ${account.balance} 円');
account.balance = -100;
}dart run bank_account.dart残高: 5000.0 円 更新後残高: 8000.0 円 エラー: 残高を負の値にすることはできません
factory コンストラクタ(シングルトンやJSONパースの実装)通常のコンストラクタは常に新しいインスタンスを生成しますが、factory コンストラクタ を使うと、既存のキャッシュ済みインスタンスを返したり、サブクラスのインスタンスを生成して返すなど、生成ロジックを柔軟に制御できます。
factory コンストラクタを使って、常に同一のプライベート静的インスタンスを返却することで、シングルトンをエレガントに実装できます。
class DatabaseService {
final String dbName;
static DatabaseService? _instance;
DatabaseService._internal(this.dbName);
// factory コンストラクタ: 常に同一のインスタンスを返す
factory DatabaseService({String dbName = 'main.db'}) {
_instance ??= DatabaseService._internal(dbName);
return _instance!;
}
}
void main() {
final db1 = DatabaseService();
final db2 = DatabaseService();
print('同一インスタンスか: ${identical(db1, db2)}');
}dart run singleton_demo.dart同一インスタンスか: true
fromJson ファクトリMap<String, dynamic> からオブジェクトを生成する fromJson を factory コンストラクタとして定義するのがDartのデファクトスタンダードです。
class Product {
final String id;
final String title;
final int price;
const Product({
required this.id,
required this.title,
required this.price,
});
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'] as String,
title: json['title'] as String,
price: json['price'] as int,
);
}
}
void main() {
final jsonMap = {'id': 'p_01', 'title': 'メカニカルキーボード', 'price': 14800};
final product = Product.fromJson(jsonMap);
print('商品: ${product.title} (¥${product.price})');
}dart run factory_from_json.dart商品: メカニカルキーボード (¥14800)
この章では、Dartのオブジェクト指向の根幹となるクラスとコンストラクタについて学びました。
this.field による簡潔な初期化が可能で、new キーワードは省略するのが標準。User.guest())、リダイレクトコンストラクタ(: this(...))、定数生成のための const コンストラクタ。super: コロン : に続くフィールド事前計算やバリデーション、super.field による親クラスへの引数転送。_ でライブラリ単位のプライベート化を実現し、get / set で安全にプロパティを公開。factory コンストラクタ: キャッシュ済みインスタンスの返却(シングルトン)や、fromJson などのカスタム生成ロジックを実現。次の 第7章 では、継承、インターフェース、Mixin、拡張メソッド(Extension)による クラスの拡張と高度な設計パターン を学びます。
口座名義と残高を管理する BankAccount クラスを実装してください。
String _owner と int _balance を持つ。BankAccount(this._owner, this._balance) を定義する(初期残高が負でないことを assert または初期化子リストで確認)。owner と balance を定義する。void withdraw(int amount) を定義し、残高が十分な場合は引き落としを行い、不足している場合は '残高不足です' と出力する。main() でインスタンスを生成し、引き落とし処理をテストする。// ここにクラスを定義してください
void main() {
// ここで動作確認を行ってください
}dart run practice6_1.dartWeb APIから受信したユーザー設定データを表す UserSettings クラスを実装してください。
final String theme(テーマ名)と final bool notificationsEnabled(通知有無)を持つ。const UserSettings({required this.theme, required this.notificationsEnabled}); を定義する。factory UserSettings.fromJson(Map<String, dynamic> json) を実装し、Mapから各プロパティをパースしてインスタンスを返す。
theme が未指定の場合は 'light'、notificationsEnabled が未指定の場合は true をデフォルト値とする。main() でJSONマップを渡し、パースされた設定内容を出力する。// ここにクラスを定義してください
void main() {
// ここで動作確認を行ってください
}dart run practice6_2.dart