この記事はDart/Flutter連載の1記事目です。
TIGの伊藤真彦です。
Dart/Flutter入門に参加します、DartといえばFlutterの話が必ずついてくるものですが、今回は連載1記事目として、敢えてプログラミング言語としてのDartに焦点を絞った記事にします。
Dartとは
DartはGoogleによって開発されたウェブ向けのプログラミング言語です、正式発表された時期は2011年です。
元々はJavaScriptの代替となることを目的に作られましたが、Javascriptのようにブラウザに統合される事なく今日まで至ります。JavaScriptの代替、という概念では競合にあたるTypeScriptが今ではGoogle社内の標準プログラミング言語として承認されています。
しかし、2018年にDart2として再起動、モバイルアプリケーション向けフレームワークであるFlutter
の基本ライブラリでDartが採用される事により、近年注目度が上昇しています。今iOS/Androidのクロスプラットフォームでのアプリケーション開発を行うならDartが熱い、という事ですね。
技術的特徴
Dartはクラスベースのオブジェクト指向言語です、単一継承のみがサポートされていますが、Mixinを利用することも可能です。
静的型付け言語としての型アノテーションが存在しつつも、dynamic型と呼ばれる特徴的な型により、動的型付け言語のようにも扱うことが可能です。上記の特徴により、大規模システムでも耐えられる堅牢さ、高パフォーマンスを維持しつつ、時には柔軟性を持つこともできる言語として設計されています。
JavaScriptトランスパイラにより、作成したコードをJavascriptに変換することが可能です。デバッグビルドでのみ動作するassert
という構文があるのも特徴です。
Dartのインストール
公式サイトにOSごとのインストールの方法がまとめられています、MACでのインストールが一番簡単です。
インストールに成功したらdart --version
コマンドでバージョンを確認できます。
~$ dart --version |
Dartの実行
dart ファイル名
で作成したDARTファイルを実行できます。
~$ dart hello.dart |
拡張子は.dart
が一般的なようですが、dartコマンドに渡す分には他の拡張子でも読み込み、実行できました。
Dartの基礎文法
基本的な文法を紹介する形でDartに触れてみます。
HelloWorld
void main() { |
Dartはmain関数に実行したい処理を書く形式で単一ファイルでとして実行可能です。
コマンドライン引数
void main(List<String> args) { |
~ % dart hello.dart Dart |
main関数に引数を持たせることでコマンドライン引数を受け取ることができます。
Goのflag.Parse()
、RubyのARGV
のようなコマンドライン引数を取り扱うための独自な手法が無い。引数の書き方が型名 変数名
の順番である、などgoに慣れた状態で触れると異文化を感じます。文字列への変数展開はJavaScriptであれば``で囲った文字列である必要があるところを’’でも問題ないあたりも細かい作法が異なりますね。
変数宣言
void main(List<String> args) { |
新しい変数はvar 変数名 = 値
の形式で宣言します。
void main(List<String> args) { |
変数の型を明示的に指定することも可能です。
void main() { |
~$ dart main.dart |
特定の形を期待しない場合はdynamic
を型アノテーションとして付けることができます。
どのような型でも再代入可能になる一方で、dynamic obj = 1;
をそのまま"1" + obj
でString型の文字列と結合することはできません。
スタイルガイドによると、ローカル変数には型アノテーション無しのvar
を、公開APIの引数等で型アノテーションを書くようにするような用法が推奨されています。
デフォルト値
void main() { |
~ % dart main.dart |
値を決めずに変数を宣言することが可能です。
void main() { |
dart main.dart |
変数のデフォルト値はどのような型であってもnullです、nullを許容しない型の変数を代入しないまま参照するとコンパイルエラーが発生します。
定数
void main() { |
const
を先頭に付与することで定数として宣言することも可能です。
定数の値を変更しようとするとコンパイルエラーCan't assign to the const variable
が発生します。
void main() { |
Dartにはfinal
という修飾子も存在します。
finalを使って宣言した変数を変更しようとするとコンパイルエラーCan't assign to the final variable
が発生します。
使い方が似ていますが、const
はコンパイル時に評価され、final
は実行段階で評価されるという違いがあります。
例えばコンパイル段階で計算できない実行時の時刻をconst
で宣言することはできません。
void main() { |
このコードは正常に動作します。
void main() { |
constに置き換えると下記のエラーが発生します。
Error compiling to JavaScript: |
このような多様な修飾子の存在はコンパイル速度のパフォーマンスチューニングに貢献しますが、若干難易度が高い印象ですね。
Dart 2.12からlate
修飾子も追加されています。
late String description; |
late
修飾子には主に2つのメリットがあります。
- 変数がnullを許容しない
- 変数を遅延評価することでパフォーマンスを改善する
条件分岐によっては利用しない値をlate
修飾子付きで宣言するような使い方が期待できます。
組み込み型
Dartには下記の組み込み型が用意されています。
- Numbers (int, double)
- Strings (String)
- Booleans (bool)
- Lists (List, also known as arrays)
- Sets (Set)
- Maps (Map)
- Runes (Runes; often replaced by the characters API)
- Symbols (Symbol)
- The value null (Null)
特徴的なものはList
、Set
の違いでしょうか。List
はお馴染みの配列であるのに対し、Set
は重複した値を持たないコレクション型です。
String
型の変数はシングルクオート、またはダブルクオート文字列を作成することが可能です。
シングルクオートとダブルクオートでは特殊文字のエスケープのルールが異なります。
var s1 = 'Single quotes.'; |
トリプルクオート
で複数行の文字列を書くことができるのが特徴的です。
var s1 = ''' |
DartにおけるSymbol
はコンパイル時常数として扱われる、文字列から生成できるデータ型です。
コンパイル時に難読化なれないため、ライブラリのメタデータの整理などに利用されますが。
ユーザー目線ではほぼ使わないようです。
enum
enum Color { |
~$ dart enum.dart |
Dartではバージョン1.8からenumがサポートされています。
比較的素朴な仕組みで、インデックスを1から始めたり飛ばしたり、文字列に変換するような機能はありません。
条件分岐
if
void main(List<String> args) { |
if文は特に違和感のないシンプルなスタイルです。
JavaScriptでは比較演算子に===
がありましたが、現在のDartでは存在しません。(初期のDartには存在していたようです)
switch
void main(List<String> args) { |
switch文も存在します。
各case毎にbreak文を設置しないと次のcaseが実行されるfall through
形式でありつつ、case内部で何かを実行したのにbreakしないとコンパイルエラーが発生するという若干癖のある仕様になっています。
つまり下記のコードはbreakが存在しないためエラーになります。
void main(List<String> args) { |
hello.dart:3:5: Error: Switch case may fall through to the next case. |
void main(List<String> args) { |
空のケースのみfall through
することで複数条件のケース文を実現するような用途が想定されています。
void main(List<String> args) { |
何か処理を実行してからfall through
することはラベルを利用することで実現できます。
~$ dart hello.dart Dart |
ループ処理
for
void main(List<String> args) { |
初期化文、条件式、後処理文の古典的なfor文が利用可能です。
forEach
void main(List<String> args) { |
forEach文でループを処理することも可能です。
item in list
void main(List<String> args) { |
Dartでは上記2種類に加えPython系のテイストを感じる書き方でもループを回すことが可能です。
while
void main(List<String> args) { |
do while
void main(List<String> args) { |
while文、do~while分も存在します。
~$ dart hello.dart Dart Flutter |
関数
void main() { |
関数を定義、実行することが可能です。
関数および関数が受け取る引数の型アノテーションはスタイルガイドで推奨されていますが、必須ではありません。
非同期処理
Dartは非同期処理をサポートしています。
Future<String> asyncFunction() { |
~$ dart aync.dart |
Future<型名>
という型アサーションで非同期処理を定義できます。
Future社員としては使いこなすモチベーションが無駄に高まります。
Future<String> asyncFunction() { |
JavaScriptのように、async
、await
の構文も利用可能です。
void main() async { |
上記のように無名関数をそのまま書いていく事も可能です。
クラス
Dartはオブジェクト指向言語であるため、クラスが存在します。
インスタンス変数、コンストラクタ、メソッドを指定して、クラスを定義することが可能です。Personクラスの定義の内部にPerson()を定義する形でコンストラクタを設定します。
コンストラクタの定義には様々なパターンが存在します。
常数コンストラクタ、ファクトリ・コンストラクタなど高度な定義方法もありますが、ひとまずは基礎的なクラス定義を紹介します。
Generative Constructors
void main() { |
コンストラクタでは引数を受け取らず適当な初期値を入力し、インスタンス生成後にメンバー変数を指定するシンプルな方式です。
void main() { |
~$ dart hello.dart |
コンストラクタに引数を定義し、初期化時に受け取る方式です。
Automatic field initialization
void main() { |
~$ dart hello.dart |
コンストラクタの記述をシンプルに定義可能な方式です。
Named Constructors
void main() { |
~$ dart hello.dart |
特定条件付きのコンストラクタを定義可能です。
Redirecting Constructors
void main() { |
~$ dart hello.dart |
他のコンストラクタの定義を再利用する方式です。
クラスの継承
void main() { |
~$ dart hello.dart |
他のオブジェクト指向言語同様クラスは継承できます。
所感
一通り基礎部分をさらってみましたが、豊富な表現力と適度な硬さの両立を目指そうとしている印象を受けました。
その辺りの思想と肌感覚がマッチすればハマる言語かもしれません。
破壊的な変更により今では動かないシンタックスが検索結果の上位に散見しているので、学習障壁を高めてしまうかなと感じました。
今回の連載で有用な記事を増やして盛り上げていきたいですね。
Dart/Flutter連載の1記事目ででした。次は宮崎さんのFlutter Swagger統合です。