【Flutter】React Native エンジニアが学ぶ Dart

2022/07/18に公開

普段は業務で React Native を使用したアプリケーションを作成しているので、そこと対比しつつ学んでいます。

今回はまず Dart 言語 から学習したのでその記録となります。

Dart

Dart is a client-optimized language for developing fast apps on any platform. Its goal is to offer the most productive programming language for multi-platform development, paired with a flexible execution runtime platform for app frameworks.

Dartは、あらゆるプラットフォームで高速なアプリケーションを開発するために、クライアントに最適化された言語です。その目標は、マルチプラットフォーム開発のための最も生産性の高いプログラミング言語と、アプリケーションフレームワークのための柔軟な実行ランタイムプラットフォームの組み合わせを提供することです。

つまり Web, ネイティブアプリ両方の開発体験をよくするための言語。WebだとJavaScriptほぼ一択になってしまうフロントエンドの代替となる期待があります。

https://dart.dev/overview

変数

Dartは静的型。変数は型推論が効くかつ、自分で型定義を記述することも可能です。

void main() {
 int age = 12;
 double height = 170.5;
 String name = "John"; // '' でも可
 bool isAdult = false;
}

var は最初の代入時に型推論されます。

void main() {
 var weight = 61.2; // double型
 weight = "70.3"; // Type Error
}

// Error: A value of type 'String' can't be assigned to a variable of type 'double'.

型宣言キーワードは他にもあります。

void main() {
 dynamic aaa = 123; // TSでいう any あらゆる型が代入可
 final bbb = "bbb"; // JSでいう const 再代入不可
 const ccc = "ccc"; // 同じく再代入不可( コンパイル時に確定)
}

条件分岐 / ループ

if / switch / for はほとんど JavaScript と同様。

void main() {
  const age = 18;
 
  if (age >= 18) {
    print("Adult");
  } else {
    print("Child");
  }
}

列挙型

主な列挙型として、List / Set / Map があります。

List

配列型。

void main() {
  List<int> cards = [1, 3, 12, 13];
  print(cards[cards.length -1]); // cards[-1] は不可
}

Set

重複不可。

void main() {
  Set<String> names = {"一郎", "二郎"};
  names.add("一郎");
  print(names); // {"一郎", "二郎"}
}

Map

key-value の 辞書型。JSでいうオブジェクト。

void main() {
  var book1 = {"title": "The Humble Programmer",
               "auther": "Edsger W. Dijkstra",
	       "price": 1200}
  print(book1["title"]);
}

関数

最初に返り値から書き、引数にも型をつけます。

int add(int a, int b) {
  return a + b;
}

アロー関数的な書き方も可能です。

int add(int a, int b) => a + b; 

[]で囲まれた引数は省略可能です。 (省略された場合はnullになります)
{}で囲むことで名前つき引数にできます。(省略された場合はnullになります)

int add1(int a, int b, [int c, int d]) => a + b;
int add2(int a, int b, {int c, int, d}) => a + b;

add2(1, 2, c: 3);

クラス

ほとんどJavaScriptと同じです。
セッターの書き方は、押さえておきたいポイントです。

class Monstar {
  // インスタンス変数
  String name;
  int defence;
  int hp;
  // コンストラクタ
  Monstar(this.name, this.defence, this.hp);
  // セッター(this.hp に新たな値を代入する)
  set protect(int damage) => hp = hp - (damage - defence);
  // ゲッター
  String get status => "Name: $name, HP: $hp";
}

void main() {
  var monstar1 = Monstar("Wild Wolf", 10, 120);
  monstar1.protect = 30;
  print(monstar1.status); // Name: Wild Wolf, HP: 100
}

継承

クラスは extendsキーワードで 継承 することができ、superで親クラスの変数やメソッドアクセスが可能です。

コンストラクタは継承できないので、再度定義してやる必要があるそうです。

class Monstar {
  // インスタンス変数
  String name;
  int defence;
  int hp;
  // コンストラクタ
  Monstar(this.name, this.defence, this.hp);
}

class Boss extends Monstar {
  // コンストラクタ
  Boss(String name, int defence, int hp): super(name, defence, hp);
}

オーバーライド

@override キーワードを使用し、親クラスのメソッドを上書きできます。

class Television {
  // ···
  set contrast(int value) {...}
}

class SmartTelevision extends Television {
  
  set contrast(num value) {...}
  // ···
}

ミックスイン

Rustの impl のようなメソッド拡張です。

mixin Jump {
  void jump() {
    print("Pyon!");
  }
}

mixin Sleep {
  void sleep() {
    print("zzz...");
  }
}

class Monstar with Jump, Sleep  {
  ...
}

インターフェース

TypeScritpの interface や Swiftのプロトコルっぽい使い方で、クラスを拡張できます。
先程の mixinclass に戻し、withの代わりに implements キーワードを使用することで、継承元クラスと同様のインターフェース実装を強要することができます。

class Jump {
  void jump() {
    print("Pyon!");
  }
}

class Sleep {
  void sleep() {
    print("zzz...");
  }
}

class Monstar implements Jump, Sleep  {
  ...
  void jump() {...}
  void sleep() {...}
}

抽象クラス

abstruct キーワードで定義される抽象クラスは、継承のみに使用でき、そのままではインスタンス化できません。

abstruct Monstar {
  ...
}

ジェネリクス

TypeScriptや、他言語と同じですので省略します。

try - catch

catch句 で エラーオブジェクトに加え、スタックトレースにアクセスすることが可能です。

void main() {
  try {
    throw "Error!"
  } catch(e, s) {
    ...
  } finally {
    ...
  }
}

enum

パターンマッチに使用できます。

enum Animal = { dog, cat, mouse }
var animal = Animal.cat;

非同期処理

async / await のコンセプトは同様で、Future キーワードを使用します。 Futureは JSの Promise に相当します。 (ちなみに、JSの Promise は元々 Futureという名前でした)

// 非同期関数は Futureを返す
Future<void> fetchUser() {
  return Future.delayed(
    const Duration(seconds: 2),
    () => print('User Info Fetched!')
  );
}

void logUserName() async{
  print("Fetching user info...");
  await fetchUser();
}
log
Fetching user info...
# ...2秒後
User Info Fetched!

ガイド

Dart公式から、ベストプラクティスのガイドが公開されています。
https://dart.dev/guides/language/effective-dart

参考

https://www.udemy.com/share/103yEU3@3A9f8JzeQVPB7BKuQRElTx5u1WMMH9FveNpmhYfotk9vO9QXs8scAOiPKW99Y9zy/

Discussion