【Dart】クラスの基本的な使い方

## 概要

Dartはオブジェクト指向言語で`Null`(Objectの子孫)以外の全てのオブジェクトがクラスのインスタンスです。
その為、Dartを学ぶ上でとても大切なクラスについて使い方をざっくりまとめました。

※概念的な説明(クラスとは・インスタンスとは)はなく、Dartでクラスを使用するメモのようなものになっています。


## クラスの宣言方法

`class` キーワードの後に宣言したいクラス名を記載することで宣言できます。

下記に具体例を示しましたが、大枠このような構成になっています。

```jsx
class クラスの名前 {
  // インスタンス変数
  // コンストラクタ
  // インスタンスメソッド
}
```

下記が実際のDartのクラスの具体例です。

インスタンス変数の宣言方法がやや異なっていますが、それぞれコメントで補足しています。

- 全てのインスタンス変数は内部的にgetterを与えられます
- finalで宣言されていないインスタンス変数は同じくsetterも与えられます

```dart
class Person {
  // インスタンス変数
  int? age; // null許容型
  final String name; // final変数
  String address = '東京都'; // 初期値を設定

  // コンストラクタ
  Person(this.name);

  // インスタンスメソッド
  void introduce() {
    print('私は$nameです');
  }
}
```

### (Named ・Factory・Redirecting)Constructors

Dartには幾つかの種類のコンストラクタが存在しているらしく、
下記の記事を見るとそれぞれの特徴、実際の使われ方がとても分かりやすく説明されていました🙇🙇

[【Dart】簡単なユーザクラスを利用してNamed,Redirecting,Factoryコンストラクタ理解](https://zenn.dev/iwaku/articles/2020-12-07-iwaku#redirecting-constructors)

## インスタンス(オブジェクト)の作成方法・変数/関数のアクセス方法

クラス名とコンストラクタを使用することでインスタンスの作成が可能です。

上記で作成したPersonクラスのインスタンスを作成してみます。

```dart
void main() {
  final taro = Person('太郎');
  taro.age = 10;
  taro.address = '京都府';

  taro.introduce(); // => 私は太郎です。京都府に住んでいる10歳です。
}
```

## 継承

`extends` キーワードを使用することでクラスを継承することができます

```dart
class Employee extends Person {
  String department;
  // superで親クラスのコンストラクタも呼んでいる
  Employee(String name, this.department) : super(name);
}
```

### 親クラスのインスタンス変数・メソッドを上書き

`@override` キーワードを使用することで親クラスのインスタンス変数やメソッドを上書きすることが可能です。

```dart
class Employee extends Person {
  String department;

  Employee(String name, this.department) : super(name);

  @override
  void introduce() {
    print('私は$departmentに所属しています。');
  }
}
```

下記が実行結果です

```dart
void main() {
  final employee1 = Employee('花子', '営業');
  employee1.introduce(); //私は営業に所属しています。
}
```

FlutterでステートレスなWidgetを作成する際に、`StatelessWidgets`等を継承して`build`メソッドをoverrideしますね。

```dart
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
  }
}
```

### 親クラスのインスタンス変数・メソッドを継承されたクラスで呼び出す

`super` キーワードで親クラスのインスタンス変数やメソッドを呼び出すことが可能です。

```dart
class Employee extends Person {
  String department;

  Employee(String name, this.department) : super(name);

  @override
  void introduce() {
    super.introduce();
    print('私は$departmentに所属しています。');
  }
}
```

下記が実行結果になります。

```dart
void main() {
  final employee1 = Employee('花子', '営業');
  employee1.age = 25;
  employee1.address = '東京都';
  employee1.introduce(); 
  // 私は花子です。東京都に住んでいる25歳です。
  // 私は営業に所属しています。
}
```

## Abstract(抽象)クラス

Abstract(抽象)クラスはインスタンス化することのできない継承されることを前提としたクラスです。

`abstract` のキーワードをclassの前につけることで宣言が可能です。

```dart
abstract class Animal {
  void walk();
}

class Dog extends Animal {
  void walk() {
    print('歩けるんだ僕は');
  }
}
```

FlutterのStatelessWidgetもAbstractクラスですね。
参照: [flutter/packages/flutter/lib/src/widgets/framework.dart](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/framework.dart#L520~L570)

```dart
abstract class StatelessWidget extends Widget {
  const StatelessWidget({ super.key });

  @override
  StatelessElement createElement() => StatelessElement(this);

  @protected
  Widget build(BuildContext context);
}
```

## Implicit Interfaces(暗黙的インターフェース)

Dartでは`Interface`といったキーワードは存在しません。
多言語(Java等)のInterfaceのような役割を行うために、`implements`キーワードを用います。

`exntends`とは異なり、クラスに組み込むとそのクラス(暗黙的インターフェース)にあるメソッドや変数を全て実装(implement)する必要があります。


```dart
class Person {
  final String name;

  Person(this.name);

  void introduce() {
    print('私は$nameです。');
  }
}

// Personの持つ全ての要素を実装(implement)してあげる必要がある
class Employee implements Person {
  final String name;
  String department;

  Employee(this.name, this.department);

  void introduce() {
    print('私は$nameで、$departmentに所属しています。');
  }
}

```


この辺の概念的な違い等は私の稚拙な説明より下記を読んで頂けると分かりやすいかと思います:bow:

- [【Dart】abstract,mixin,extends,implements,with等の使い方基礎](https://zenn.dev/iwaku/articles/2020-12-16-iwaku)
- ["extends" versus "implements" versus "with"](https://stackoverflow.com/questions/55295782/extends-versus-implements-versus-with)
- [DART - DIFFERENCES BETWEEN EXTENDS, IMPLEMENTS AND MIXIN](https://www.topcoder.com/thrive/articles/dart-differences-between-extends-implements-and-mixin)

## Mixin

`mixin`キーワードを使用することで拡張するメソッドや状態を定義することが可能です。
`with`キーワードを使用することで実際に定義したmixinを使用することができます。

```dart
mixin Accounting {
  void fileTaxies() {
    print('あ、確定申告代わりにしときましたよ');
  }

  void askAccountant() {
    print('税理士に聞いてみましょう.....');
  }
}

class Person with Accounting {
  final String name; // finalは初期化後に変更不可

  Person(this.name);
}
```

実行結果は下記です

```dart
void main() {
  final taro = Person('太郎');
  taro.fileTaxies(); //あ、確定申告代わりにしときましたよ
}
```

実際にmixinが役に立つケースの説明等に関しては[こちら](https://resocoder.com/2019/07/21/mixins-in-dart-understand-dart-flutter-fundamentals-tutorial/)がとても分かりやすかったです。

## クラスメソッド・変数
`static`キーワードを使用することでクラスメソッド・変数を定義することが可能です。

```dart
class Person {
  static const initialAge = 0;
  static void printFeature() {
    print('足が２つ。腕も２つ。');
  }
}

print(Person.initialAge) //=> 0
Person.printFeature() //=> '足が２つ。腕も２つ。'
```

## 参考

- [A tour of the Dart languageのclasses](https://dart.dev/guides/language/language-tour#classes)を読みながら書いたのでそちらを見て頂くのが正確です。
- さらに細かい正確な情報は[Dart language specification](https://dart.dev/guides/language/spec)を見て頂ければと思います