【Dart】Dynamic型とObject型について

Dynamic型とObject型について調べてみた。

Dartのdynamic型は、コンパイル時に型チェックが行われないため、実行時エラーのリスクが高まる点で注意が必要。<br>
代わりにObject型を使用することで、より安全なコードを書くことができる。

下記コードを例にする


```
void main() {
  // 動作確認用 dynamic 型
  dynamic valueDynamic = 'Hello, Dynamic!';
  print("dynamic型の初期値: $valueDynamic");
  valueDynamic = 10;
  print("dynamic型の変更後: $valueDynamic");
  
  // dynamic型ではエラーは出ない
  print("dynamic型の数値が偶数か否か: ${valueDynamic.isEven}");
  valueDynamic = 'Hello, Dynamic!';
  print("dynamic型の変更後: ${valueDynamic.length}");
  

  // 動作確認用 Object 型
  Object valueObject = "Hello, Object!"; // Object 型に文字列を代入
  print("Object型の初期値: $valueObject");
  valueObject = 10;
  print("Object型の変更後: $valueObject");
  
  // Object型ではエラーが発生する
  print("Object型の数値が偶数か否か: ${valueObject.isEven}");
  valueObject = 'Hello, Object!';
  print("Object型の文字列長さ: ${valueObject.length}");

  // 型チェックすればOK
  if (valueObject is String) {
    print("Object型の文字列長さ: ${valueObject.length}");
  }
}

```


### dynamic型の場合
初期値として代入された valueDynamic は数値 10 なので、この時点では isEven プロパティの呼び出しは有効です。<br>

しかし、valueDynamic が別の型（例えば文字列）に変更された場合、isEven プロパティはサポートされていないため、実行時にエラーが発生する。<br>

valueDynamic が文字列であれば length プロパティは使用可能。
しかし、もし valueDynamic が数値や他の型に変更された場合、length プロパティは存在しないため、やはり実行時エラーが発生する。

### Object型の場合
dynamic型と違い、isEven プロパティやlength プロパティを使用するとエラーが発生する。<br>これは型チェックが必要な為。<br>型チェックを行うと上記プロパティが使用できる。<br>
つまり、型チェックを行うことが前提なので、dynamic型と比較し、安全なコードを書くことができる。<br><br><br>

### 【参考】Object型の使い道
Object型はDartのすべての型のスーパータイプであり、型安全性を維持しつつ、汎用的な操作を行いたい場合に使用されます。

`Object`型の具体的な用途について解説します。`Object`型はDartの**すべての型のスーパータイプ**であり、型安全性を維持しつつ、汎用的な操作を行いたい場合に使用されます。その用途を以下に詳しく説明します。

---

### 型が特定できない汎用的なデータを扱う場合
プログラムを設計していると、**特定の型に依存せず、汎用的にデータを扱いたい場面**が出てきます。`Object`型を使用すれば、どの型の値でも扱うことができ、型安全性を維持しつつ柔軟な操作が可能です。

#### **例: 汎用的な関数**
引数や戻り値に`Object`型を使用することで、どのような型の値にも対応可能な関数を作成できます。

```dart
void printObject(Object obj) {
  print('値: $obj');
  print('型: ${obj.runtimeType}');
}

void main() {
  printObject(42);             // 出力: 値: 42, 型: int
  printObject("Hello, Dart!"); // 出力: 値: Hello, Dart!, 型: String
  printObject([1, 2, 3]);      // 出力: 値: [1, 2, 3], 型: List<int>
}
```

- **用途:**  
  - デバッグやロギング用の汎用関数。
  - 入力データの型に依存しない処理。

---

### Mapやリストなどのコレクションで多様な型を格納する場合
Dartのコレクション（`List`, `Map`など）を使用する場合、デフォルトでは`Object`型を使用して、**異なる型のデータを格納**できます。

#### **例: `Map<String, Object>` を使用する**
異なる型のデータを格納する場合に`Object`型を使えば柔軟なデータ構造を作成できます。

```dart
void main() {
  Map<String, Object> data = {
    'name': 'Alice',       // String型
    'age': 30,             // int型
    'isStudent': false,    // bool型
    'grades': [90, 85, 88] // List<int>型
  };

  print(data); // 出力: {name: Alice, age: 30, isStudent: false, grades: [90, 85, 88]}

  // 値の型に応じた処理
  data.forEach((key, value) {
    print('$key: $value (型: ${value.runtimeType})');
  });
}
```

- **用途:**  
  - JSONやフォームデータなど、異なる型を持つデータ構造を扱う場合。
  - キーバリュー形式で多様なデータを格納する際の柔軟性。

---

### 型を事前に特定せずに操作したい場合
`Object`型を使用すれば、型を事前に特定する必要がありません。ただし、型に応じた操作を行う場合は、**型チェック（`is`）や型キャスト（`as`）**を使用する必要があります。

#### **例: 型に応じた操作**
`Object`型の値に対して、型をチェックし、適切な処理を行います。

```dart
void handleObject(Object obj) {
  if (obj is int) {
    print('整数: $obj, 偶数かどうか: ${obj.isEven}');
  } else if (obj is String) {
    print('文字列: $obj, 長さ: ${obj.length}');
  } else if (obj is List) {
    print('リスト: $obj, 要素数: ${obj.length}');
  } else {
    print('その他の型: $obj');
  }
}

void main() {
  handleObject(42);              // 出力: 整数: 42, 偶数かどうか: true
  handleObject("Hello, Dart!");  // 出力: 文字列: Hello, Dart!, 長さ: 12
  handleObject([1, 2, 3]);       // 出力: リスト: [1, 2, 3], 要素数: 3
  handleObject(true);            // 出力: その他の型: true
}
```

- **用途:**  
  - 型に応じた動的な振る舞いを実現する。
  - 型が多様なデータを柔軟に処理する。

---

### フレームワークやライブラリでの汎用的な引数や戻り値
フレームワークやライブラリでは、汎用的なAPIを提供するために`Object`型が使用されることが多いです。たとえば、Dartの`Future`や`Stream`クラスでは、非同期処理の結果として`Object`型の値を返すことができます。

#### **例: 非同期処理での`Object`型**
非同期処理で多様な型の値を扱う場合に`Object`型を使用します。

```dart
Future<Object> fetchData() async {
  // 任意の型のデータを返す
  return "サーバーからのデータ";
}

void main() async {
  Object result = await fetchData();
  print('取得したデータ: $result');
}
```

- **用途:**  
  - 汎用的なライブラリやAPIの設計。
  - 非同期処理やイベント駆動型プログラムで多様な型を扱う。


---

### Null安全の下での使用
Dartでは`Object`型は`null`を許容しません（`Object?`型を使用する必要があります）。これは、`dynamic`型や`Object?`型と区別される特徴です。

#### **例: Null安全**
```dart
void main() {
  Object value = "Hello"; // OK
  // value = null; // エラー: The value 'null' can't be assigned to a variable of type 'Object'.

  Object? nullableValue = null; // OK
  print(nullableValue); // 出力: null
}
```

- **用途:**  
  - Null安全なコードを記述する際に、`Object`型を使用して型安全性を高める。


---


### まとめ
・dynamic型は、コンパイル時に型チェックが行われないため非推奨
・Object型は、特定の型に依存せず、汎用的にデータを扱いたい場面で使用する
・基本的には型を明示的に指定したい