目次

クラス

Dartは、クラスとmixinベースの継承を持つオブジェクト指向言語です。 Nullを除くすべてのオブジェクトはクラスのインスタンスであり、Nullを除くすべてのクラスはObjectを継承します。 mixinベースの継承とは、(トップクラスである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を省略できます。たとえば、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.
}

初期化されていないインスタンス変数は、nullable型で宣言されている場合、値はnullになります。nullableでないインスタンス変数は、宣言時に初期化されなければなりません

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

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回だけ設定する必要があります。final、非lateインスタンス変数は、宣言時、コンストラクタパラメータを使用するか、コンストラクタの初期化子リストを使用して初期化します。

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

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

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

暗黙的インターフェース

#

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

クラスは、implements句で1つ以上のインターフェースを宣言し、インターフェースで要求される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);
}

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