2020/8/27 Dart : A tour of the Dart language>>Classesの訳

Classes

Dartの全てのオブジェクトはクラスのインスタンスであり、全てのクラスはObjectクラスから派生します。

Objectクラスを除く全てのクラスが一つのスーパークラスを持ちます(多重継承はできない。)が、それにも関わらず、クラスのボディは複数のクラス階層の中で再利用できます。これが「Mixinベースの継承」です。

Extension methodsを使うとクラス定義を変えずに、またサブクラスも作らずに機能を特定のクラスに追加することができます。


Using class members

オブジェクトは関数とデータから構成されるメンバーを持ちます(それぞれメソッドとインスタンス変数と呼びます。)あなたがメソッドを呼び出す時、オブジェクトにおいてメソッドを実行します。そのメソッドはそのオブジェクトの関数(メソッド)とデータ(インスタンス変数)にアクセスできます。

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

var p = Point(2, 2);

// Set the value of the instance variable y.
p.y = 3;

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

// Point型の変数pのdistanceToメソッドを実行します。↓
double distance = p.distanceTo(Point(4, 4));

.の代わりに?.を使うと、?.の左側の被演算子がnullの時に例外が出るのを防げます。

class Point{
  int x=3;
  int y=5;
  
  Point(this.x,this.y);
}

void main(){
  var p1=Point(2,2);
  p1=null;
  print(p1.y); //p1の値はnullなので、nullのフィールドyを参照していることになる。
}

/*(下記のエラー発生。)
Uncaught TypeError: Cannot read property 
'get$y' of nullError: 
TypeError: Cannot read property 'get$y' of null
*/

class Point{
  int x=3;
  int y=5;
  
  Point(this.x,this.y);
}

void main(){
  var p1=Point(2,2);
  p1=null;
  print(p1?.y);  //?.を使うとエラーは出ない。
}

/*
null
*/

Using constructors

コンストラクタを使ってオブジェクト(インスタンス)を生成します。コンストラクタ名は「クラス名」あるいは「クラス名.識別子名」のどちらかです。識別子名を使うことで複数の種類のコンストラクタを定義できます。例えば、下記のコードではPoint()コンストラクタとPoint.fromJson()コンストラクタを使ってPointインスタンスを生成しています。

class Point{
  int x,y;
  Point(this.x,this.y);
  Point.fromJson(Map<String,dynamic> data){
    x=data['x'];
    y=data['y'];
  }
}

void main(){
  var p1 = Point(2, 2);

  var p2 = Point.fromJson({'x': 10, 'y': 20});
}

constant constructorがあるクラスもあります。constant constructorを使うとコンパイル時定数を生成できます。下のサンプルのように、コンストラクタ名の前にconstキーワードを付けます。

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final double x, y;

  const ImmutablePoint(this.x, this.y);
}

void main(){
var a =  const ImmutablePoint(1, 1);
var b =  const ImmutablePoint(1, 1);
print(identical(a, b));  //aとbは同じインスタンス
assert(identical(a, b));
}

/*
true
*/

二つの同一のコンパイル時定数を生成することは、結果として同一のインスタンスを参照することになります。

// 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キーワード以外は省略できます。

上記サンプルで左辺のconst以外全てのconstキーワードが省略できている訳だが、pointAndLine変数がconst(コンパイル時定数)ということは、必然的に右辺のマップもコンパイル次定数である必要がある。(右辺の値がコンパイル時に決定されないと、左辺もコンパイル時に決定できないため)

ということはマップの各要素も必然的にコンパイル時定数である必要がある。

だからそういう場所はconstキーワードを省略してもいいですよ、ということだと思われる。

こういう風に左辺でconstキーワードを使うことで、自動的(必然的)にコンパイル時定数であることが求められるような場所、そういう流れを

一つ下で「constant context(constantの文脈)」という言葉で表現している、と思われる。それに関して何の説明もない(笑)

どこか他で説明しているのだろうか。


class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final double x, y;

  const ImmutablePoint(this.x, this.y);
}

var a = const ImmutablePoint(1, 1); // Creates a constant
var b = ImmutablePoint(1, 1); // Does NOT create a constant
//↑のコンストラクタImmutablePoint()は必ずconst constructorでないといけない
//訳ではない。そういう場所のことを"outside of a constant context"
//と表現しているのだと思われる。 

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

//クラス定義でconstant constructorを定義していても、
//bのようにコンストラクタ実行時にconstキーワードを付けないと、
//constantオブジェクトは生成されない。

constant context(constantの文脈)でない場所でconstキーワードを使わずにconst constructorを使った場合、constantでないオブジェクトが生成されます。

逆にいうと、const constructorとして定義しているコンストラクタを(const constructorでない)普通のコンストラクタとして呼び出すこともできる、ということになる。


Getting an object’s type

実行時のオブジェクトの型を取得したい場合runtimeTypeフィールドを使用します。runtimeTypeフィールドはType型のオブジェクトを返します。

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

ここまでクラスの使い方を見てきました。このセクションの残りはクラスの実装(定義)の仕方についてです。


Instance variables

インスタンス変数の定義のしかたを以下に示します。

class Point {
  double x; // インスタンス変数xは初期化されていないのでnullがセットされます。
  double y; // インスタンス変数yもnullがセットされます。
  double z = 0; // インスタンス変数zを0で初期化しています。
}

インスタンス変数を初期化しないと自動的にnullがセットされます。

全てのインスタンス変数は暗黙的なゲッターメソッドが生成されます。finalでないインスタンス変数は自動的に暗黙的なセッターメソッドが生成されます。

なのでゲッター・セッターを自分で用意しなくても以下のようなコードが書けます。

class Point {
  double x;
  double y;
  //ゲッター・セッターは用意していない
}

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.
}

クラス定義内のインスタンス変数宣言時に(コンストラクタの中では無い)インスタンス変数を初期化したら、インスタンス生成時に値がセットされます。これはコンストラクタやイニシャライザリストの実行前にセットされます。

 

参考

https://dart.dev/guides/language/language-tour#classes

https://dart.dev/guides/language/language-tour#using-class-members

https://dart.dev/guides/language/language-tour#using-constructors

https://dart.dev/guides/language/language-tour#getting-an-objects-type

https://dart.dev/guides/language/language-tour#instance-variables

コメントを残す

メールアドレスが公開されることはありません。