クラス
Dart は、クラスとミックスインベースの継承を持つオブジェクト指向言語です。すべてのオブジェクトはクラスのインスタンスであり、Null を除くすべてのクラスは ObjectObject? を除く)が 1 つの超クラスを持つ一方で、クラス本体を複数のクラス階層で再利用できることを意味します。拡張メソッドは、クラスを変更したりサブクラスを作成したりすることなく、クラスに機能を追加する方法です。クラス修飾子を使用すると、ライブラリがクラスをサブタイプ化する方法を制御できます。
クラスメンバーの使用
#オブジェクトは、関数とデータ(それぞれメソッドとインスタンス変数)で構成されるメンバーを持ちます。メソッドを呼び出すと、オブジェクト上でそれを呼び出します。メソッドは、そのオブジェクトの関数とデータにアクセスできます。
インスタンス変数またはメソッドを参照するには、ドット (.) を使用します。
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 の場合に例外を回避するには、. の代わりに ?. を使用します。
// If p is non-null, set a variable equal to its y value.
var a = p?.y;コンストラクタの使用
#コンストラクタを使用してオブジェクトを作成できます。コンストラクタ名は、ClassName または ClassName.identifier のいずれかです。たとえば、次のコードは Point() および Point.fromJson() コンストラクタを使用して Point オブジェクトを作成します。
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});次のコードは同じ効果がありますが、コンストラクタ名の前にオプションの new キーワードを使用しています。
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});一部のクラスは定数コンストラクタを提供します。定数コンストラクタを使用してコンパイル時定数を作成するには、コンストラクタ名の前に const キーワードを付けます。
var p = const ImmutablePoint(2, 2);2 つの同一のコンパイル時定数を構築すると、単一の正規インスタンスが生成されます。
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // They are the same instance!定数コンテキスト内では、コンストラクタまたはリテラルの前の const を省略できます。たとえば、定数マップを作成する次のコードを見てください。
// 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 キーワードの使用以外はすべて省略できます。
// Only one const, which establishes the constant context.
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};定数コンストラクタが定数コンテキストの外にあり、const なしで呼び出された場合、**非定数オブジェクト**が作成されます。
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 を使用できます。
print('The type of a is ${a.runtimeType}');ここまでは、クラスの使用方法を見てきました。このセクションの残りの部分では、クラスの実装方法を示します。
インスタンス変数
#インスタンス変数を宣言する方法は次のとおりです。
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 を参照してください。
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 にアクセスできません。
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 ではないインスタンス変数は、宣言時、コンストラクタパラメータを使用するか、コンストラクタの初期化子リストを使用して初期化します。
class ProfileMark {
  final String name;
  final DateTime start = DateTime.now();
  ProfileMark(this.name);
  ProfileMark.unnamed() : name = '';
}コンストラクタ本体の開始後に final インスタンス変数の値を代入する必要がある場合は、次のいずれかを使用できます。
- ファクトリコンストラクタを使用します。
- late finalを使用しますが、注意してください:初期化子を持たない- late finalは、API にセッターを追加します。
暗黙的なインターフェース
#すべてのクラスは、クラスのすべてのインスタンスメンバーと、実装しているインターフェースのメンバーを含むインターフェースを暗黙的に定義します。クラス A が B の実装を継承せずに B の API をサポートするようにしたい場合は、クラス A は B インターフェースを実装する必要があります。
クラスは、implements 句で宣言し、インターフェースによって要求される API を提供することによって、1 つ以上のインターフェースを実装します。たとえば、次のようになります。
// 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()));
}クラスが複数のインターフェースを実装することを指定する例を次に示します。
class Point implements Comparable, Location {
  ...
}クラス変数とメソッド
#static キーワードを使用して、クラス全体で共有される変数とメソッドを実装します。
静的変数
#静的変数(クラス変数)は、クラス全体の状態と定数に役立ちます。
class Queue {
  static const initialCapacity = 16;
  // ···
}
void main() {
  assert(Queue.initialCapacity == 16);
}静的変数は、使用されるまで初期化されません。
静的メソッド
#静的メソッド(クラスメソッド)はインスタンス上で動作しないため、this にアクセスできません。ただし、静的変数にはアクセスできます。次の例が示すように、クラス上で静的メソッドを直接呼び出します。
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);
}静的メソッドをコンパイル時定数として使用できます。たとえば、定数コンストラクタに静的メソッドをパラメータとして渡すことができます。