読者です 読者をやめる 読者になる 読者になる

堕(惰)プログラマ開発記録

タイトル変えようかなとも思ってるけれど,思い浮かばない

Dart基礎文法最速マスター(?)

Dart

基礎文法最速マスター系は2年ぐらい前に流行ったものなので,とっても遅いのですが.
私自身が,Dartの勉強をしながら,なんとなくまとめておいたものを公開します.
間違っている部分などなど有ると思いますが,訂正とか頂けると嬉しいです.
気が向いたら,段々追加していくかも知れません.

1.Hello World

Dartの入出力にはcoreライブラリの中にある,print関数を使用します.
そのとき"$変数名"とするとその変数の内容を埋め込むことができます.

main() {
  print("Hello World");
}
main() {
  String str = "Hello World";
  print("$str");
}

どちらも,Hello Worldを表示します.

Hello World

2.コメント

行コメント

print("Hello World"); //コメント

ブロックコメント

/*
コメント
*/
print("Hello World");

3.リテラル

nullリテラル

nullオブジェクトは組み込みクラスNullの唯一のインスタンスです.
さらに,エラーを出すことなく,どんな物にでも代入できるようにされています.
null上のメソッドを呼び出してしまった時,"NullPointerException"という例外が投げられます.

int i = null; // i をnullリテラルで初期化
if( i == null)  {
  print("変数iは空です!");
}
リテラル

リテラルは,10進数・16進数の整数と倍精度の10進数です.

255;
0xff;
0XFF;
25500e-2;
255.00;

//上記はすべて,10進数の255を表します
ブール値リテラル

予約語の true と false はブール値の 真 と 偽 を表します.
この2つの予約語でboolを実装しています.

文字リテラル

シングルクォート(')またはダブルクォート(")に括られた文字列です.
エスケープシーケンスを用いることもできます.但し,クォート直前にアットマーク(@)を付与した場合,エスケープシーケンスは無視されます.

String str = "H";

"abcde"                           // abcde
'abcde'                           // abcde
"First Line\nSecond Line\n"       // First Line(改行)Second Line(改行)
'Alphabet of the eighth is $str'  // Alphabet of the eighth is H
"${str}ello World";               // Hello World
リストリテラル

リストを"[ n1, n2, n3... n]"のように表すことができます.
このとき,constを直前に付与し,"const[...]"とした場合,これは定数リストとして処理されます.
リストはオブジェクトを列挙する場合(集合)に適しています.

マップリテラル

マップを"{ キー1 : 値1 , キー2 : 値2... キーn : 値n }"と表すことができます.
このとき,constを直前に付与し,"const{...}"とした場合,これは定数マップとして処理されます.
マップはキーと値を対応させて持っているので,キーから値を呼び出すことが可能です.
キーは必ず文字列,値はnullでも構いません.

4.変数

Dartでは,"final 型 変数名 = 値;"のように変数を宣言します*1
finalをつけない場合,値の変更と初期値の省略を行うことができます.
初期化されない変数はnullで有ることが保証されています.

main()  {
  final int    i = 123456789012345; // 整数型intの変数iを宣言し,値を割り当てる
  final String s = "abcdefg";       // 文字列型Stringの変数sを宣言,割り当て

  int x;                            // final無しで宣言した場合,
  x == null;                        // true
  
  x = 1000;                         // 途中で代入を行う事ができる
}
型のない変数

Dartでは変数に型を与えるか与えないかは自由です.
どういう意味かというと,intなどといった型を与える他にJavascriptなどで見られるvarを使用して変数を宣言することができます.但し,finalとは共存できません.
すると上のDartコードの後半は,次のように書き換えられます.

main()  {
  var x;
  x = 1000;
}

5.型

Dartには以下のような型が用意されています(一部です)。

int    : 整数型(...-3,-2,-1,0,1,2,3...)
double : 浮動小数点数型(3.1415926535... etc.)
String : 文字列型("a","abcdefghi" etc.)
bool   : 論理型( true , false )
List   : リスト
Set    : 集合
Queue  : キュー
Map    : 辞書

Dartにおけるすべての型は

  • Iterable
  • Map
  • Comparable
  • Hashable
  • Pattern

の一つ以上を実装したものです.*2


変数の項で述べましたとおり,Dartでは型を指定するかどうかは任意です.
型指定をせず,varを使用して変数を書くこともできます.
この記事の中で今後,varを使用して書く場合があるかも知れません.

6.関数

関数は一連の処理に名前をつけることで,よく使う機能を簡単に呼び出すことができます.
"戻り値の型 変数名( 仮引数1リスト ){ 処理 }"と書く一般的な方法と,
"戻り値の型 変数名( 仮引数1リスト ) => 式"と書くこともできます.

//引数を取らない関数
int alphabet_nums()  {
  //"return 値;"で値を返します.この値は「戻り値の型」と一致している必要があります.
  return 26;
}

//引数1つ取る関数
int cube(int x)  {
  return x * x * x;
}

//引数2つ取る関数
double average(double x,double y)  {
  return (x+y)/2;
}

これらの作成した関数は,"関数名( 引数リスト )"のように呼び出すことができます.

main()  {
  //戻り値を変数に入れる
  final int    i   = alphabet_nums(); // iは26になる
  final int    v1  = cube(5);         // v1は一辺5の立方体の体積(=125)になる
  final int    v2  = cube(6);         // v2は一辺6の立方体の体積(=216)になる
  final double ave = average(v1,v2);  // aveはv1とv2の平均になる

  print("アルファベットは $i個 あります.");
  print("一辺5の立方体の体積は $v1 になります.");
  print("一辺6の立方体の体積は $v2 になります.");
  print("一辺5の立方体の体積と一辺6の立方体の体積の平均は $ave になります.");
}
何も返さない関数

しばしば,何も返さない関数を作りたくなる場合があります.
そのときは戻り値の型をvoidにします.下記はprintを内包し,Stringに特化するdisp関数です.

void disp(final String str)  {
  print(str);
}

main()  {
  disp("Hello World");
}
仮引数の省略

関数の仮引数は省略することができます.その場合,仮引数を"["と"]"で括ります.
またデフォルトの値を指定することができ,指定がない場合はnullが収納されます.

void character_detail(String name, [String work = "NEET"])  {
  print("$name's work is $work.");
}

main()  {
  character_detail("Taro","Business Man");  //Taro's work is Business Man. 
  character_detail("Jiro");                 //Jiro's work is NEET.
}
型の省略

また,戻り値の型や仮引数の型を省略して書くこともできます.
先ほどのaverage関数を型指定なしで書くと以下のようになります.
(但し,'/'が定義されていないStringなどでは例外が投げられます)

average(x,y)  {
  return (x+y)/2;
}

さらに短く書くことができ,一行で済ませることもできます.

average(x,y) => (x+y)/2;

7.基本演算

四則演算
final int n = 2 + 5;  // 7
final int n = 2 - 5;  // -3
final int n = 2 * 5;  // 10
final int n = 9 / 4;  // 2
final int n = 9 % 4;  // 1
論理演算
final int a = 1;
final int b = 2;

final bool r = a == b; // false
final bool r = a != b; // true
final bool r = a < b;  // true
final bool r = a > b;  // false
final bool r = a <= b; // true
final bool r = a >= b; // false

final int p = true;
final int q = false;

final bool r = p && q; // false
final bool r = p || q; // true

8.制御構文

if-else/条件演算子

if-else文や,条件演算子を使用して条件分岐することができます.

// if-elseを使用した条件分岐
// "if ( 条件 ){ 処理 } else{ それ以外の処理 }"
num max(num x, num y)  {// num型は,int型とdouble型,両方で動作させることができます.
  if(x > y)  {
    return x;
  }
  else  {
    return y;
  }
}
// 条件演算子を使用した条件分岐
// "条件 ? trueの時の処理 : falseの時の処理"
num max(num x, num y)  {
  return x > y ? x : y;
}
switch

switch文は値と処理を対応させることができます.
"switch ( 値 ){ case 〜: 処理 break; }"

String review(int rate)  {
  switch (rate)  {
    case 1: return "不満";
    case 2: return "やや不満";
    case 3: return "普通";
    case 4: return "まあ満足";
    case 5: return "満足";

    default: return "不明";    // デフォルト動作を設けることが可能です
  }

  // この関数の場合,すべての場合でreturnされるため,ここに来ることはありません
}
while

while文はある動作を繰り返します.
"while( 条件 ){ 処理 }"

int counter=1;
while(counter <= 20)  {
  print("$counter 回目");
  ++counter; //counter = counter + 1; のようなもの
}
for

whileを書きやすくしたものがforです.
"for( 初期化式; 条件式; 再初期化式 ) { 処理 }"

for(int counter=1; counter <= 20; ++counter)  {
  print("$counter");
}

上記のwhileとforのコードは同じ動作をします.このように書き換えることが可能です.

さらにfor...inやforEachを使用することもできます.
以下の2つのコードはどちらも,list内の要素をすべてprintに渡します.

var list = [1,1,2,3,5,8,13];

//for...inを使う方法
for (final x in list)  {
  print(x);
}

//forEachを使う方法
list.forEach(print);

9.クラスとインターフェース

クラスの基本

クラスによって新しい型を定義することができます.

class Point  {
  num _x, _y; // アンダーバー(_)から始まるメンバーはprivate
  
  // ゲッター(getを使って定義できるし,継承後,スーパークラスのゲッターをオーバーライドできる)
  num get x() => _x;
  num get y() => _y;

  // セッター(setを使って定義できるし,継承後,スーパークラスのセッターをオーバーライドできる)
  void set x(num x){this._x = x;}
  void set y(num y){this._y = y;}
  
  // コンストラクタ
  // また,Point(num x, num y) : this._x = x, this._y  = y; と同じ.
  Point(this._x,this._y);
 
  // こんなに長くてもおk
  num distance(Point other)
    => Math.sqrt((Math.pow((other.x/*ゲッター呼び出し*/)-this._x,2) + Math.pow((other.y/*ゲッター呼び出し*/)-this._y,2)));
}

main()  {
  Point a = new Point(1,5);                 // コンストラクタ"Point(this._x,this._y)"を呼び出す
  Point b = new Point(10,2);
  
  num distance = a.distance(b);             // Pointクラスのnum distance(Point other)を呼び出す
  print(distance);                          // 点a,点b間の距離 = 9.486832980505138
}
クラスの継承

クラスを継承し,継承元のクラスのメンバ変数,メソッドを利用することができます.
その継承元のクラスをスーパークラスと言います.

例えば,上のクラスを継承したPoint3Dというクラスを作ってみることとします.

class Point3D extends Point  { // extendは継承の宣言です
  num _z;
  num get z() => _z;
  void set z(num z){this._z = z;}

  // コンストラクター
  // 派生元のPointのコンストラクタを呼び出す必要があります
  // "super(x,y)"でスーパークラス(Point)のコンストラクタを呼び出しています
  Point3D(num x,num y, num this._z) : super(x,y);

  //スーパークラスのメソッドをオーバーライドすることもできます
  num distance(Point3D other)
    => Math.sqrt(Math.pow((other.x-this._x),2) + Math.pow((other.y-this._y),2) + Math.pow((other.z-this._z),2));
}

main()  {
  Point3D a = new Point3D(1,10,8);          // コンストラクタ"Point3D(num x,num y, num this._z)"を呼び出す
  Point3D b = new Point3D(10,2,5);
  
  num distance = a.distance(b);             // Pointクラスのnum distance(Point other)を呼び出す
  print(distance);                          // 点a,点b間の「3次元上」の距離 = 12.409673645990857
}
ファクトリコンストラクタ

コンストラクタに名前をつけたりすることで,複数コンストラクタを定義することが可能です.
コンストラクタ名にfactoryをつけるとファクトリを作れ,構築する際,他のコンストラクタを呼び出して返すことができます.

例えば,以下のクラスは,上記のPointコンストラクタの引数としてString型も取ることが出来るように書き換えたものです.

class Point  {
  num _x, _y;
  num get x() => _x;
  num get y() => _y;
  
  // コンストラクタ
  Point(this._x,this._y);

  // ファクトリーコンストラクタ!!
  // 今回はこのコンストラクタにstrという名前をつけました(オーバーロードできないため)
  factory Point.str(String x_str,String y_str)  {
    return new Point(Math.parseDouble(x_str),Math.parseDouble(y_str));
  }
 
  num distance(Point other)
    => Math.sqrt((Math.pow((other.x)-this._x,2) + Math.pow((other.y)-this._y,2)));
}

main()  {
  Point a = new Point.str("1","5");         // ファクトリコンストラクタ"factory Point.str(String x_str,String y_str)"を呼び出す
  Point b = new Point(10,2);                // コンストラクタ"Point(this._x,this._y)"を呼び出す
  
  num distance = a.distance(b);             // Pointクラスのnum distance(Point other)を呼び出す
  print(distance);                          // 点a,点b間の距離 = 9.486832980505138 -> 結果は一緒!
}
定数コンストラクタ

定数を作るためのコンストラクタで,通常のコンストラクタの前にconstをつけたものです.
定数コンストラクタが有るクラスはfinalでないメンバを持つことができず,コンパイル時定数のみを渡すことができます.

したがって,たびたび出てくるPointクラスを以下のように定数コンストラクタを定義し,実行時演算量を減らしてみましょう.

class Point  {
  final num x, y;
  
  // 定数コンストラクタ!!
  const Point(this.x,this.y);
 
  num distance(Point other)
    => Math.sqrt((Math.pow((other.x)-this.x,2) + Math.pow((other.y)-this.y,2)));
}

main() {
  Point a = const Point(1,5);               // 定数コンストラクタ"const Point(this.x,this.y)"を呼び出す
  Point b = const Point(10,2);              // 変数を定義するときも new でなく, const を用いる
  
  num distance = a.distance(b);             // Pointクラスのnum distance(Point other)を呼び出す
  print(distance);                          // 点a,点b間の距離 = 9.486832980505138 -> 結果は一緒!
}
staticメンバ

また,staticをつけることで静的なメソッドや変数を定義できるのは,Dartも同じです.
staticを付けられた変数,メソッドインスタンス化することなく,自由に扱えます.

class FourOperator  {
  static final double PI = 3.1415926; // 静的定数
  static num add(num x,num y) => x+y; // 静的メソッド
  static num sub(num x,num y) => x-y; // 静的メソッド
  static num mul(num x,num y) => x*y; // 静的メソッド
  static num div(num x,num y) => x/y; // 静的メソッド
}

main()  {
  print(FourOperator.PI);           // 3.14154926
  print(FourOperator.add(10,2));    // 12
  print(FourOperator.sub(10,2));    // 8
  print(FourOperator.mul(10,2));    // 20
  print(FourOperator.div(10,2));    // 5
}
インターフェース

インターフェースはクラスと密接な関係を持っています.
インターフェースでは宣言しか行うことができません.実装はすべて,classでimplementsした後,class内に行います.
また,インターフェースにnewを行い,インスタンス化することは出来ません.

// Shapeインターフェースを実装するクラスはperimeterを持っているということ
// 「こういうものは"num perimeter()"を持っていなくてはいけない」などと,大まかに解りやすいコードが書ける
interface Shape  {
  num perimeter(); 
}

// Shapeを実装したRectangleクラス = num perimeter()を実装しないといけない : implements = 実装
class Rectangle implements Shape  {
  final num _height, _width;
  Rectangle(num this._height, num this._width);  
  num perimeter() => 2*_height + 2*_width;
}

// Shapeを実装したSquareクラス = num perimeter()を実装しないといけない : implements = 実装
class Square implements Shape  {
  final num _side;
  Square(num this._side);
  num perimeter() => 4*_side;
}

このクラスは,以下のように使用することができます.

main()  {
  var rec = new Rectangle(10,20);
  final int per1 = rec.perimeter();
  print("縦10,横20の長方形の周囲の長さは $per1 です.");

  var squ = new Square(8);
  final int per2 = squ.perimeter();
  print("一辺8の正方形の周囲の長さは $per2 です.");
}

10.ジェネリック

Javaなどと同様,ジェネリックスを使うことができます.
Dartは型づけするかしないかは自由なので,よく分からないかも知れませんが
coreライブラリのMapやListなどもジェネリックスです.

class container<T>  {
  final T _value;
  container(T this._value);
  T get_value() => _value;
}

main()  {
  var i = new container(123456);         // var = container<int>
  print(i.get_value());

  var d = new container(3.141592);       // var = container<double>
  print(d.get_value());

  var s = new container("Hello World");  // var = container<String>
  print(s.get_value());
}

11.例外(Exception)

Dartでは,処理中に異常が出たとき,現在の処理を中止して例外を発生させることができます.
Dartで投げられる例外はcoreライブラリのExceptionインターフェースを継承,または実装して出来ています.
基本的な例外の使用方法は以下のようになっています.

main()  {
  try  {
    //ここで例外が発生するかもしれないコードを記述
  
    //例えば
    int i = null;
    i.toString(); //例外発生!

    //自分で何かがあったとき投げてもいい
    int natural_number = -50;
    if(natural_number <= 0)  { // 「自然数に0は入る!」ってキレたら負け
      throw new Exception("自然数じゃない!"); // throwするのはException以外でもおkだよ
    }
  }
  catch(Exception e)  {
    // ここで例外を受け取れる
    // catchの引数にふさわしいものに引き渡されるため,catch節はいくつあっても構わない

    print("例外だって!");
  }
  finally  {
    // finally節は例外の有無に関わらず,最後に必ず実行される
  }
}

tryに対応するcatchまたはfinallyが1節以上存在するように書けば良いので,catchを1つ書けばfinallyを書く必要はありません.
また,catchに該当する型がなかった場合,さらに外のcatchに飛びます.
catchやfinallyの中で再throwでき,さらに外のcatchに渡すこともできます.

書いてみて

まああれだ,Javaの機能を独自に変化させてたり,まんま引っ張ってきてる文法多いね.
Dartの強みは「任意で型を明示したりしなかったり」できるとこじゃないかなぁ.
少なくてもこれで,Javascriptのvarが受け入れられない人をWeb系に引っ張れるんだから.(godai_0519←引っ張られた人)

*1:アキラさんの方法を参考して,こうしています.私自身もこのほうが間違いが少ないと思ったので.final ≠ constですが!

*2:参考: https://sites.google.com/site/dartrefjp/about/technical_overview#libraries