Skip to Content
ドキュメントFlutterコースクラスを理解する

クラスを理解する

このページで学ぶこと

  • クラスとオブジェクト指向の考え方がつかめる
  • インスタンスフィールドを持つクラスを書ける
  • 3 種類のコンストラクタ(generative / named / factory)を使い分けられる
  • extends を使ってクラスを継承できる

なぜクラスを学ぶのか

Flutter のコードを見ると、必ず class という単語が出てきます。

class MyApp extends StatelessWidget { ... }
class Counter extends Notifier<int> { ... }
dart

Flutterはオブジェクト指向(Object-Oriented, OO)の考え方で設計されています。

オブジェクト指向のざっくりした考え方

オブジェクト指向は「データ(フィールド)と、そのデータに対する操作(メソッド)を一つの箱にまとめよう」という発想です。例えば「ユーザー」を扱うとき:

// ❌ データと操作がバラバラ
String userName = 'とんぺい';
int userAge = 20;
 
String greet(String name) => 'こんにちは、$name さん';
bool isAdult(int age) => age >= 18;
dart

これをクラスにまとめると、以下のようになります。

// ✅ データと操作を User クラスにまとめる
class User {
String name;
int age;
 
User(this.name, this.age);
 
String greet() => 'こんにちは、$name さん';
bool isAdult() => age >= 18;
}
 
final user = User('とんぺい', 20);
print(user.greet()); // こんにちは、とんぺいさん
print(user.isAdult()); // true
dart

インスタンス変数(フィールド)

クラスの中に書いた変数を**インスタンス変数(フィールド)**と呼びます。

class User {
    String name; // フィールド
    int age; // フィールド
 
    User(this.name, this.age);
}
dart

finalで書き換え不可にする

一度決めたら変えられたくない値に変数修飾子finalをつけます。

class User {
    final String name;
    final int age;
 
    User(this.name, this.age);
}
 
final user = User('とんぺい', 20);
 
// user.name = 'ぺーとん'; // final nameなのでコンパイルエラー
dart

ゲッターとセッター

ゲッター:値を「計算して返す」メソッド

ゲッター(“getter”)は、フィールドの皮を被せた関数のようなものです。呼び出し側からは普通のフィールドと同じように見えます。 通常の関数の場合、呼び出されるたびに計算(「評価」)しますが、ゲッターの場合は呼び出されても最初に計算した値をいい感じに使い回してくれます(「キャッシュ」)。

セッター:値を「受け取って反映する」メソッド

書き込みはできるが、読み出しはできないフィールドを更新するのに用いられたりします。

class Temperature {
    double _celsius = 0; //プライベート
 
    set celsius(double value) {
        if (value < -273.15) {
            throw ArgumentError("絶対零度よりはい温度は設定できません");
        }
 
        _celsius = value;
    }
}
 
final temp = Temperature();
t.celsius = 25; //set celsiusが呼ばれる
dart
class Rectangle {
    final int width;
    final int height;
 
    Rectangle(this.width, this.height);
 
    int get area => width * height;
    bool get isSquare => width == height;
}
 
final rect = Rectangle(4, 3);
final area = rect.area; // 12
final isSquare = rect.isSquare; // false
dart

いつ使う?

ざっくりこんな使い分けになります。

やりたいこと書き方
値をそのまま公開したいだけ普通のフィールド
他のフィールドから計算した値 を見せたいゲッター(get
内部は _xxx に隠し、読み取りだけ許したい_xxx + ゲッター(セッターなし)
フィールド書き込みだけ許し、読み出しはできないようにしたい_xxx + セッター(ゲッターなし)
書き込み時に バリデーションや変換 を挟みたいゲッター + セッター

Dartのコンストラクタ

コンストラクタは、クラス(設計図のようなもの)からインスタンス(実態)を作るときに呼ばれる特別な関数です。以下にDartでよく使うコンストラクタの書き方を紹介します。

① Generative コンストラクタ(基本形)

class User {
final String name;
final int age;
 
// this.xxx と書くと、引数をそのままフィールドに代入できる
User(this.name, this.age);
}
 
final user = User("とんぺい", 20);
dart

this.nameは「引数で受け取った値をそのままthis.nameに代入する」というショートカット記法です。 長く書くと

User(String name, int age): this.name = name, this.age = age;
dart

② Named コンストラクタ(名前付きコンストラクタ)

コンストラクタに名前を付けて複数用意できます。用途ごとにインスタンスの生成手順を変えたいときに便利です。

class User {
    final String name;
    final int age;
 
    User(this.name, this.age);
 
    User.guest(): name = 'ゲスト', age = 0;
    User.fromJson(Map<String, dynamic> json): name = json['name'] as String,
    age = json['age'] as int;
}
 
final u1 = User('とんぺい', 20);
final u2 = User.guest();
final u3 = User.fromJson({'name': 'ぺーとん', 'age': 18});
dart

③ Factory コンストラクタ

factory を付けると、コンストラクタの中で return を自由に書ける ようになります。
generative/namedコンストラクタが「その場で新しいインスタンスを作る」のに対し、factoryコンストラクタは「なにを返すか自由にコントロールできる」のが違いです。

class User {
    final String name;
    final int age;
 
    factory User.fromInput(String? name, int? age) {
        if (name == null || age == null) {
            return User("ゲスト", 0);
        }
        return User(name, age);
    }
}
 
final u1 = User.fromInput('とんぺい', 20); // User('とんぺい', 20)
final u2 = User.fromInput(null, null); // User('ゲスト', 0)
dart

継承(extends

クラスには「別のクラスを土台にして、新しいクラスを作る」仕組みがあります。これを継承と呼びます。
Dartでは extends を使って書きます。

なぜ継承を使うのか

例えば、「動物」というクラスと「犬」というクラスを考えてみましょう。
犬も動物の一種なので、名前や年齢は共通して持っています。これを毎回書き直すのは面倒です。

// ❌ 同じフィールド・メソッドを何度も書いている
class Animal {
    final String name;
    Animal(this.name);
    void breathe() => print('$name は呼吸している');
}
 
class Dog {
    final String name; // Animalと同じ…
    Dog(this.name);
    void breathe() => print('$name は呼吸している'); // Animalと同じ…
    void bark() => print('$name: ワン!');
}
dart

extends を使うと、共通部分を 親クラス(スーパークラス) にまとめられます。

// ✅ Animal を継承して Dog を作る
class Animal {
    final String name;
    Animal(this.name);
    void breathe() => print('$name は呼吸している');
}
 
class Dog extends Animal {
    Dog(super.name); // 親のコンストラクタに name を渡す
 
    void bark() => print('$name: ワン!');
}
 
final dog = Dog('ポチ');
dog.breathe(); // ポチ は呼吸している(Animalから継承したメソッド)
dog.bark();    // ポチ: ワン!(Dog独自のメソッド)
dart

super で親クラスを呼ぶ

super は「親クラス」を指すキーワードです。コンストラクタやメソッドの中で使います。

class Animal {
    final String name;
    Animal(this.name);
    void introduce() => print('私は $name です');
}
 
class Dog extends Animal {
    final String breed; // 犬種
 
    // 親のコンストラクタを super(...) で呼ぶ
    Dog(String name, this.breed) : super(name);
 
    void introduce() {
        super.introduce(); // 親の introduce() を先に呼ぶ
        print('犬種は $breed です');
    }
}
 
final dog = Dog('ポチ', '柴犬');
dog.introduce();
// 私は ポチ です
// 犬種は 柴犬 です
dart

補足: Dog(super.name) と書くと、: super(name) を省略できます(Dart 2.17以降)。短く書けるので最近はこちらがよく使われます。

@override でメソッドを上書きする

親クラスのメソッドを子クラスで書き換えたいときは @override をつけます。

class Animal {
    final String name;
    Animal(this.name);
    void sound() => print('$name: …');
}
 
class Cat extends Animal {
    Cat(super.name);
 
    @override
    void sound() => print('$name: ニャー');
}
 
final cat = Cat('タマ');
cat.sound(); // タマ: ニャー
dart

Flutterでの例

実は、Flutterのコードで出てくる extends StatelessWidget もまったく同じ仕組みです。

// StatelessWidget を継承して、自分のWidgetを作っている
class MyApp extends StatelessWidget {
    const MyApp({super.key}); // 親のコンストラクタに key を渡す
 
    @override
    Widget build(BuildContext context) {
        return const MaterialApp(home: Text('Hello'));
    }
}
dart

ここまで読めば、extends StatelessWidget が「StatelessWidget を土台にして、MyApp という新しいクラスを作っている」という意味だとわかりますね。

まとめ

やりたいこと書き方
別のクラスを土台にして新しいクラスを作るclass 子 extends 親
親のコンストラクタを呼ぶsuper(...) または super.xxx
親のメソッドを呼ぶsuper.メソッド名()
親のメソッドを上書きする@override をつけて同名で定義

確認しよう

  • フィールドとコンストラクタを持つクラスを書けた
  • generative / named / factory の3種類のコンストラクタを使い分けられた
  • extends で別のクラスを継承できた
  • @override でメソッドを上書きできた

AIに聞いてみよう

「Dartの extendsimplementswith の違いを初心者向けに教えてください」


次のステップ

Widgetの基本

Last updated on