[Flutter/Dart] 定数の宣言 finalとconstの違い

定数の宣言

Flutter もとい Dartには、多くの言語と同じように2種類の定数の宣言方法があります。定数とは、一度宣言したら、後で再代入できないものです。こちらは、その違いは何で、どのように使い分けていけば良いでしょうか。

final/const

// 使い方
final int value1 = 1;
final value2 = 2;

const int value3 = 3;
const value4 = 4;

// 以下はコンパイルエラー
value1 = 5;
value3 = 6

final とconst の違い

finalは実行時に定数を確定させます。一方でconstはコンパイル時に定数を確定させます。いやあ、よく分かりませんね。
逆に言うと、constはコンパイル時に値が決まっていないといけないのです。
例えば、クラス内にメンバー変数をfinalとして定義しますが、constにはできません。コンストラクタで引数として、外側から値を与えられるので、コンパイル実施時には値が決まらないからです。
一方で、クラス変数としてはstatic constという形で宣言できます。こちらは、コンパイル実行時に値が決まるので、constにできます。

class TestClass{
  final int memberValue;
  static const int classValue = 1;
  TestClass(this.memberValue);
}

なるべくconstを使おう

constはコンパイル時に値が決定されます。そのため、再生成しないことが決まりますので、メモリーの確保が一度ですむので、動作が軽くなります。
よく言われるのが、FlutterのWidgetはなるべくconstにした方が良い、ということでしょう。一度生成されたら、再生成されることがないことが保証されています。そのため、該当のWidgetを再生成する必要するかどうか、Flutter側で判断するコストが軽くなり、また、そのWidgetの子Widgetを再生する必要があるか判断する必要性もなくなります。そのため、全体としてアプリが軽くなります。

Listではちょっと動作が異なる

さて、以下のコードを実行すると、どうなるでしょうか。

final finalList = [1, 2, 3];
finalList.add(4);

const constList = const [1, 2, 3];
constList.add(4);

constList.add(4)で、UnsupportedError(“Cannot add to an unmodifiable list”) が発生します。
finalはあくまで再代入できないだけなので、finalList.add(4)が実行できます。finalで宣言したクラスでも、メンバー変数を変更できますよね。
一方で、constの場合は、コンパイル実行時に値が定数化されているので、実行時に例外が発生します。(残念ながら、コンパイル時にエラー検出してくれない)

まとめ

以上でconstとfinalの違いを説明しました。私も昔C#で検索したなぁ、と懐かしく思ってます。参考になれば、幸いです。