メインコンテンツにスキップ

クラス

Dart は、クラスとミックスインベースの継承を持つオブジェクト指向言語です。すべてのオブジェクトはクラスのインスタンスであり、Null を除くすべてのクラスは Object から派生します。ミックスインベースの継承とは、すべてのクラス(トップクラスである Object? を除く)が 1 つの超クラスを持つ一方で、クラス本体を複数のクラス階層で再利用できることを意味します。拡張メソッドは、クラスを変更したりサブクラスを作成したりすることなく、クラスに機能を追加する方法です。クラス修飾子を使用すると、ライブラリがクラスをサブタイプ化する方法を制御できます。

クラスメンバーの使用

#

オブジェクトは、関数とデータ(それぞれメソッドインスタンス変数)で構成されるメンバーを持ちます。メソッドを呼び出すと、オブジェクト上でそれを呼び出します。メソッドは、そのオブジェクトの関数とデータにアクセスできます。

インスタンス変数またはメソッドを参照するには、ドット (.) を使用します。

dart
var p = Point(2, 2);

// Get the value of y.
assert(p.y == 2);

// Invoke distanceTo() on p.
double distance = p.distanceTo(Point(4, 4));

左側のオペランドが null の場合に例外を回避するには、. の代わりに ?. を使用します。

dart
// If p is non-null, set a variable equal to its y value.
var a = p?.y;

コンストラクタの使用

#

コンストラクタを使用してオブジェクトを作成できます。コンストラクタ名は、ClassName または ClassName.identifier のいずれかです。たとえば、次のコードは Point() および Point.fromJson() コンストラクタを使用して Point オブジェクトを作成します。

dart
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});

次のコードは同じ効果がありますが、コンストラクタ名の前にオプションの new キーワードを使用しています。

dart
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});

一部のクラスは定数コンストラクタを提供します。定数コンストラクタを使用してコンパイル時定数を作成するには、コンストラクタ名の前に const キーワードを付けます。

dart
var p = const ImmutablePoint(2, 2);

2 つの同一のコンパイル時定数を構築すると、単一の正規インスタンスが生成されます。

dart
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // They are the same instance!

定数コンテキスト内では、コンストラクタまたはリテラルの前の const を省略できます。たとえば、定数マップを作成する次のコードを見てください。

dart
// Lots of const keywords here.
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};

最初の const キーワードの使用以外はすべて省略できます。

dart
// Only one const, which establishes the constant context.
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};

定数コンストラクタが定数コンテキストの外にあり、const なしで呼び出された場合、**非定数オブジェクト**が作成されます。

dart
var a = const ImmutablePoint(1, 1); // Creates a constant
var b = ImmutablePoint(1, 1); // Does NOT create a constant

assert(!identical(a, b)); // NOT the same instance!

オブジェクトの型の取得

#

実行時にオブジェクトの型を取得するには、Type オブジェクトを返す Object プロパティ runtimeType を使用できます。

dart
print('The type of a is ${a.runtimeType}');

ここまでは、クラスの使用方法を見てきました。このセクションの残りの部分では、クラスの実装方法を示します。

インスタンス変数

#

インスタンス変数を宣言する方法は次のとおりです。

dart
class Point {
  double? x; // Declare instance variable x, initially null.
  double? y; // Declare y, initially null.
  double z = 0; // Declare z, initially 0.
}

null許容型で宣言された初期化されていないインスタンス変数は、値 null を持ちます。null許容でないインスタンス変数は、宣言時に初期化する必要があります

すべてのインスタンス変数は、暗黙的なgetterメソッドを生成します。final ではないインスタンス変数と、初期化子を持たない late final インスタンス変数も、暗黙的なsetterメソッドを生成します。詳細については、getter と setter を参照してください。

dart
class Point {
  double? x; // Declare instance variable x, initially null.
  double? y; // Declare y, initially null.
}

void main() {
  var point = Point();
  point.x = 4; // Use the setter method for x.
  assert(point.x == 4); // Use the getter method for x.
  assert(point.y == null); // Values default to null.
}

late ではないインスタンス変数を宣言された場所で初期化すると、コンストラクタとその初期化子リストが実行される前に、インスタンスが作成されたときに値が設定されます。その結果、late ではないインスタンス変数の初期化式(= の後)は this にアクセスできません。

dart
double initialX = 1.5;

class Point {
  // OK, can access declarations that do not depend on `this`:
  double? x = initialX;

  // ERROR, can't access `this` in non-`late` initializer:
  double? y = this.x;

  // OK, can access `this` in `late` initializer:
  late double? z = this.x;

  // OK, `this.x` and `this.y` are parameter declarations, not expressions:
  Point(this.x, this.y);
}

インスタンス変数は final にすることができ、その場合、正確に 1 回設定する必要があります。finallate ではないインスタンス変数は、宣言時、コンストラクタパラメータを使用するか、コンストラクタの初期化子リストを使用して初期化します。

dart
class ProfileMark {
  final String name;
  final DateTime start = DateTime.now();

  ProfileMark(this.name);
  ProfileMark.unnamed() : name = '';
}

コンストラクタ本体の開始後に final インスタンス変数の値を代入する必要がある場合は、次のいずれかを使用できます。

暗黙的なインターフェース

#

すべてのクラスは、クラスのすべてのインスタンスメンバーと、実装しているインターフェースのメンバーを含むインターフェースを暗黙的に定義します。クラス A が B の実装を継承せずに B の API をサポートするようにしたい場合は、クラス A は B インターフェースを実装する必要があります。

クラスは、implements 句で宣言し、インターフェースによって要求される API を提供することによって、1 つ以上のインターフェースを実装します。たとえば、次のようになります。

dart
// A person. The implicit interface contains greet().
class Person {
  // In the interface, but visible only in this library.
  final String _name;

  // Not in the interface, since this is a constructor.
  Person(this._name);

  // In the interface.
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// An implementation of the Person interface.
class Impostor implements Person {
  String get _name => '';

  String greet(String who) => 'Hi $who. Do you know who I am?';
}

String greetBob(Person person) => person.greet('Bob');

void main() {
  print(greetBob(Person('Kathy')));
  print(greetBob(Impostor()));
}

クラスが複数のインターフェースを実装することを指定する例を次に示します。

dart
class Point implements Comparable, Location {
  ...
}

クラス変数とメソッド

#

static キーワードを使用して、クラス全体で共有される変数とメソッドを実装します。

静的変数

#

静的変数(クラス変数)は、クラス全体の状態と定数に役立ちます。

dart
class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

静的変数は、使用されるまで初期化されません。

静的メソッド

#

静的メソッド(クラスメソッド)はインスタンス上で動作しないため、this にアクセスできません。ただし、静的変数にはアクセスできます。次の例が示すように、クラス上で静的メソッドを直接呼び出します。

dart
import 'dart:math';

class Point {
  double x, y;
  Point(this.x, this.y);

  static double distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var distance = Point.distanceBetween(a, b);
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}

静的メソッドをコンパイル時定数として使用できます。たとえば、定数コンストラクタに静的メソッドをパラメータとして渡すことができます。