Dartは、オブジェクト指向のプログラミング言語で、Googleによって開発されました。Dartは、Webアプリケーションやモバイルアプリケーションの開発に適しており、柔軟性と高速性が特徴です。
このチュートリアルでは、Dartプログラミング言語の基本的な構文と機能について学ぶことができます。初心者から中級者まで、プログラミングのスキルレベルに関わらず、誰でもこのチュートリアルから学習を開始することができます。
__TOC__
DartはGoogleによって開発されたオブジェクト指向のプログラミング言語です。Webアプリケーションやモバイルアプリケーションの開発に使用されています。
Dartは静的型付けの言語で、ライブラリやフレームワークが豊富に用意されています。FlutterというUIフレームワークもDartで書かれており、Flutterにより、多くの種類のプラットフォーム向けにアプリケーションを開発することができます。
オブジェクト指向：Dartは、完全にオブジェクト指向の言語です。それは、すべてがオブジェクトであるということを意味します。
型安全：Dartは、静的型付け言語です。つまり、コンパイル時に型エラーを検出できます。
非同期処理：Dartは、非同期処理に優れています。それは、コールバック関数を使用せずに非同期処理を実現できるasync/awaitキーワードを提供しています。
ビルトイン型：Dartは、多くのビルトイン型を提供しています。例えば、int、double、String、List、Map、Setなどです。
変数の宣言：Dartでは、varキーワードを使用して変数を宣言できます。また、finalキーワードを使用して定数を宣言することもできます。
ラムダ：Dartは、ラムダ式をサポートしています。それは、簡潔な構文で関数を定義することができるということを意味します。
クラス：Dartは、クラスをサポートしています。それは、オブジェクト指向プログラミングの基本的な要素の1つです。
DartPad というサイトを使うと、自分の環境にインストールしないでもブラウザ上で Dart のプログラミングが行なえます。
⇒ https://dart.dev/#try-dart
[https://dart.dev/get-dart Get the Dart SDK] を入手できます。
Windowsの場合は、公式インストール方法ではパッケージマネージャーの [https://chocolatey.org/ Chocolatey] を使うのですが日本では馴染みがないようなので、これとは別にコミュニティによる用意されたインストーラを使うと良いでしょう。⇒ https://gekorm.com/dart-windows/
dartのバージョンは、
 dart --version
で確認できます。
 PS C:\Users\user1> dart --version
 Dart SDK version: 2.15.0-168.0.dev (dev) (Thu Sep 30 12:23:13 2021 -0700) on "windows_x64"
のようにバージョン番号が出たら、成功です。
コード例
main() {
  print("Hello world");
}
print関数などのよく使われる関数はdart:coreライブラリにありますが、dart:coreライブラリはimport不要で使えます。
実行方法は、コマンドプロンプトあるいはWindows Terminalで、
 dart ファイル名.dart
です。
Dartが初めてのプログラミング言語という人は少ないでしょうから、他の言語にはあまりないDart固有の機能をまとめて紹介します。
Dartではnull安全性が組み込まれており、変数がnullを許容するかどうかを明示的に指定します。例えば、int型の変数にnullを代入するためにはint?という型を使用します。この機能により、nullに関連するエラーやバグを減らすことができます。
 
int? nullableInt = null;
int nonNullableInt = 42;
以下は、Dart 2.12で追加されたNull安全性の機能を含んだコードの例です。Null安全性はDartの重要な変更点の一つであり、?や!などの新しい機能が導入されました。
 
void main() {
  String? nullableString = "Hello"; // ?を使ってnullが許容される文字列型の変数を宣言
  if (nullableString != null) {
    print(nullableString.length); // nullチェック後のnullableStringのプロパティに安全にアクセス
  } else {
    print("String is null");
  }
  String nonNullableString = nullableString ?? "Fallback"; // nullの場合に代替値を設定する方法
  print(nonNullableString);
  String nonNullableString2 = nullableString!; // nullチェックをバイパスし、非nullを保証する演算子
  print(nonNullableString2);
}
この例では、String? nullableStringというnullableな文字列型変数を宣言し、if文でnullチェックして安全にプロパティにアクセスしています。また、??演算子を使用してnullableな変数がnullの場合の代替値を設定し、!演算子を使ってnullチェックをバイパスして非nullを保証しています。
Dartには他の言語にはあまり見られない、拡張メソッドという機能があります。これにより、既存のクラスに新しいメソッドを追加することができます。
extension StringExtension on String {
  int get doubleLength => this * 2;
}
void main() {
  String text = "Hello";
  print(text.doubleLength); // 出力: 10
}
Dartではリストやマップ、セットなどのコレクションを簡潔に初期化するための構文があります。
 
// リストの初期化
List numbers = [1, 2, 3, 4, 5];
// マップの初期化
Map ages = {
  'John': 30,
  'Alice': 25,
  'Bob': 35,
};
// セットの初期化
Set fruits = {'apple', 'banana', 'orange'};
これにより、コレクションを宣言して初期化する手順を簡素化できます。
クラスのコンストラクタがフィールドの初期化しか行わない場合、{this.field}のような記述でコンストラクタを省略できます。
class Person {
  final String name;
  final int age;
  Person(this.name, this.age); // コンストラクタの省略
}
これにより、フィールドの初期化を簡潔に表現できます。
Dartには整数型や浮動小数型などの型があります。変数の宣言のときに型を明示する方法と、初期化する値から型を推論する方法があります。
main() {
  int a = 3;
  int b = 2 * a;
  print(b);
}
実行結果
6
Dartでは、型は var 宣言を使うことで型推論させることができます。
main() {
  var a = 4;
  var b = 1.23;
  print(a + b);
}
実行結果
5.23
main() {
  final List a = [1, 2, 3];
  const List b = [2, 3, 5];
  print(a + b);
  a[1] = 9;
  print(a + b);
  // b[0] = 0; ← const で宣言すると指し示す先のオブジェクトの値も変更できないので、この文はエラー
}
実行結果
[1, 2, 3, 2, 3, 5] 
[1, 9, 3, 2, 3, 5]
finalで宣言すると、初期化以降値を変更できない変数を作ることができます。しかし、finalで宣言された変数の参照先の値は変更できます。
constで宣言すると、初期化以降値を変更できない変数を作ることができます。また、constで宣言された変数の参照先の値も変更できません。
文字列の中に、式を埋め込むことができます。
コード例
main() {
  var num = 4;
  print("num + 3 is ${num + 3}\n");
}
実行結果
num + 3 is 7
文字列の中に ${ 式 } の形式で埋込むと、実行時に展開されます。
文字列の中に埋込む式が単一の変数ならばより簡略化した形式で表現できます。
コード例
main() {
  var num = 4;
  print("num is $num\n");
}
実行結果
num is 4
文字列の中に $変数名 の形式で埋込むと、実行時に展開されます。
改行などに対するバックスラッシュ・エスケープ・シーケンスは他のプログラミング言語と同様に使えます。
「\」そのものを表示したい場合は、先頭にもうひとつ「\」をつけて「\\」のようにするだけです。
コード例
main() {
  print("\\n");
}
実行結果
\n
以下は、Dartにおける一般的な型とそれぞれのリテラル例を示した一覧表です。
これらはDartでよく使われる基本的な型の一覧です。それぞれの型は異なる種類のデータを表し、リテラル例はその型の値を表す例示です。
void main() {
  num a = 10;
  int b = 5;
  double c = 3.14;
  String name = "John Doe";
  bool isTrue = true;
  List numbers = [1, 2, 3, 4, 5];
  Map ages = {
    "John": 36,
    "Jane": 25,
    "Bob": 42,
  };
  // 変数の値を表示
  print('Value of a: $a');
  print('Value of b: $b');
  print('Value of c: $c');
  print('Value of name: $name');
  print('Value of isTrue: $isTrue');
  // リストの要素を表示
  print('Elements in numbers list:');
  for (var number in numbers) {
    print(number);
  }
  // マップの要素を表示
  print('Entries in ages map:');
  ages.forEach((key, value) {
    print('$key is $value years old');
  });
  // 関数の実行結果を表示
  int result = multiply(b, 6);
  print('Result of multiplication: $result');
}
// 関数の定義
int multiply(int x, int y) {
  return x * y;
}
実行結果:
Value of a: 10
Value of b: 5
Value of c: 3.14
Value of name: John Doe
Value of isTrue: true
Elements in numbers list:
1
2
3
4
5
Entries in ages map:
John is 36 years old
Jane is 25 years old
Bob is 42 years old 
Result of multiplication: 30
Dartには、コレクションの要素を特定の条件に基づいて動的に生成するためのgenerateメソッドがあります。これはリストやマップを生成する際に便利な方法です。
List myList = List.generate(5, (index) => index * 2);
print(myList); // 出力: [0, 2, 4, 6, 8]
この例では、List.generateメソッドを使用して、要素数が5個で、各要素がインデックスに2を掛けた値で初期化されたリストを生成しています。
generateメソッドの構文は次のようになります。
List List.generate(int count, E Function(int index) generator, {bool growable: true})
count: 生成する要素の数を指定します。
generator: 要素を生成するための関数です。この関数は引数として現在のインデックスを受け取り、その位置の要素を返します。
growable: リストが変更可能かどうかを指定します。デフォルトではtrueです。
同様に、generateメソッドはマップを生成するためにも使用できます。
Map myMap = Map.fromEntries(
  List.generate(5, (index) => MapEntry(index, 'Value $index')),
);
print(myMap); // 出力: {0: Value 0, 1: Value 1, 2: Value 2, 3: Value 3, 4: Value 4}
この例では、0から4までのキーを持ち、それぞれの値が'Value 0'から'Value 4'まで連番になるマップを生成しています。
Dartのコレクション（リスト、マップ、セットなど）を操作するために便利なイテレーションメソッドがいくつか用意されています。以下はその一部です。
forEach: 各要素に対して特定の処理を行います。
List numbers = [1, 2, 3, 4, 5];
numbers.forEach((number) => print(number)); // 各要素を出力
map: 各要素を変換して新しいリストを作成します。
List numbers = [1, 2, 3, 4, 5];
List doubled = numbers.map((number) => number * 2).toList(); // 各要素を2倍にして新しいリストを作成
where: 条件に一致する要素のみをフィルタリングします。
List numbers = [1, 2, 3, 4, 5];
List evenNumbers = numbers.where((number) => number % 2 == 0).toList(); // 偶数のみを抽出
reduce: 要素を結合して1つの値に縮約します。
List numbers = [1, 2, 3, 4, 5];
int sum = numbers.reduce((value, element) => value + element); // 要素の合計を計算
forEach: 各エントリー（キーと値のペア）に対して特定の処理を行います。
Map ages = {'John': 30, 'Alice': 25, 'Bob': 35};
ages.forEach((key, value) => print('$key is $value years old')); // 各エントリーを出力
map: キーと値を変換して新しいマップを作成します。
Map ages = {'John': 30, 'Alice': 25, 'Bob': 35};
Map greetings = ages.map((key, value) => MapEntry(key, 'Hello, $key!')); // キーに挨拶を追加した新しいマップを作成
keys / values: キーのコレクション / 値のコレクションを取得します。
Map ages = {'John': 30, 'Alice': 25, 'Bob': 35};
Iterable names = ages.keys; // キーのコレクションを取得
Iterable values = ages.values; // 値のコレクションを取得
これらのイテレーションメソッドは、コレクションの要素を効果的に操作し、必要な処理を実行するために利用されます。
void main() {
  // 算術演算子
  int num1 = 10;
  int num2 = 5;
  print('num1 + num2 = ${num1 + num2}'); // 和
  print('num1 - num2 = ${num1 - num2}'); // 差
  print('num1 * num2 = ${num1 * num2}'); // 積
  print('num1 / num2 = ${num1 / num2}'); // 商
  print('num1 % num2 = ${num1 % num2}'); // 余り
  // 比較演算子
  int a = 10;
  int b = 20;
  print('a == b : ${a == b}'); // aとbが等しいか？
  print('a != b : ${a != b}'); // aとbが等しくないか？
  print('a > b  : ${a > b}');  // aがbより大きいか？
  print('a < b  : ${a < b}');  // aがbより小さいか？
  print('b >= a : ${b >= a}'); // bがa以上か？
  print('b <= a : ${b <= a}'); // bがa以下か？
  // 論理演算子
  bool val1 = true;
  bool val2 = false;
  print('val1 && val2 : ${val1 && val2}'); // val1とval2が両方ともtrueか？
  print('val1 || val2 : ${val1 || val2}'); // val1とval2のどちらかがtrueか？
  print('!val1 : ${!val1}'); // val1の否定
  // 代入演算子
  int x = 10; 
  print('x = $x'); // xの初期値を表示
  x += 5; // xに5を加算
  print('x += 5 : $x'); // 結果を表示
  x -= 3; // xに3を減算
  print('x -= 3 : $x'); // 結果を表示
  x *= 4; // xに4を乗算
  print('x *= 4 : $x'); // 結果を表示
  // 条件付き演算子
  int age = 20;
  String message = (age < 18) ? '未成年です' : '成人です';
  print('message : $message'); // 結果を表示
}
コメントをソースコードに書くときは、//や、/*と*/を使用します。
下記コードのように//を使うと行末までがコメントになります。
コード例:
main() {
  var cat = "Miku"; // 猫の名前
  print("The name of the cat is ${cat} \n");
}
実行結果
The name of the cat is Miku
下記コードのように/*と*/で文を囲むと2行以上をコメントにできます。
コード例:
main() {
  /* 動物の種類
     猫
     犬
     兎
  */
  var animals = ["cat", "dog", "rabbit"];
  print(animals);
}
実行結果
[cat, dog, rabbit]
関数は戻値の型と関数名と仮引数リストで定義します。
関数の呼出しは、関数名と実引数リストを与えます。
コード例:
main() {
  // add関数に引数として1と2を指定する。
  print(add(1, 2));
  // sub関数に引数として1と2を指定する。
  print(sub(1, 2));
}
int add(int x, int y) {
  int z = x + y;
  return z;
}
int sub(int x, int y) => x - y;
実行結果:
3
-1
このコードでは、add関数とsub関数が定義されています。add関数はxとyの和を計算し、その結果を返します。一方、sub関数はxからyを引いた結果を返します。main関数では、これらの関数を引数を指定して呼び出し、結果を出力しています。
add関数は通常のブロック構文で定義されており、sub関数はアロー構文を使用して短く表記されています。このような表記法は、1行で簡潔に関数を定義する場合に便利です。
Dartには、様々な用途に特化した多くのライブラリーが存在します。以下は、Dartでよく使用されるライブラリーの一部です。
これらのライブラリー以外にも、様々な目的に特化したライブラリーが存在しています。Dartの公式ドキュメントや、Dart Pubと呼ばれるパッケージマネージャーで検索することで、必要なライブラリーを見つけることができます。
print関数などのよく使われる関数はdart:coreライブラリーにあり、dart:coreライブラリーはimportが不要ですが、それ以外のライブラリーはimportが必要です。
コード例:
import 'dart:math';
num numval = pi;
main() {
  print(sin(pi / 4));
}
実行結果
0.7071067811865475
ライブラリーのimport:
import 'dart:math';
num型:
num numval = pi;
num型は、数値ならば整数型でも浮動小数点数型でも保持できる変数を宣言します。
ライブラリー関数の使用:
  print(sin(pi / 4));
Dartの型システムの説明の前なので、簡単な例の説明に留めました。
Dartには、if-else文、for文、while文、do-while文、switch文、forEachメソッドなど、多様な制御構造があります。
Dartプログラミング言語の主要な制御構造のチートシート :
void main() {
  // if文
  int x = 10;
  if (x > 5) {
    print("x is greater than 5");
  } else if (x == 5) {
    print("x is equal to 5");
  } else {
    print("x is less than 5");
  }
  
  // forループ
  for (int i = 0; i < 5; i++) {
    print("i is $i");
  }
  
  // for-inループ
  List numbers = [1, 2, 3, 4, 5];
  for (int number in numbers) {
    print("number is $number");
  }
  
  // whileループ
  int count = 0;
  while (count < 5) {
    print("count is $count");
    count++;
  }
  
  // do-whileループ
  int i = 0;
  do {
    print("i is $i");
    i++;
  } while (i < 5);
  
  // switch文
  String color = "red";
  switch (color) {
    case "red":
      print("color is red");
      break;
    case "green":
      print("color is green");
      break;
    case "blue":
      print("color is blue");
      break;
    default:
      print("color is not recognized");
      break;
  }
  
  // break文
  for (int i = 0; i < 5; i++) {
    if (i == 3) {
      break;
    }
    print("i is $i");
  }
  
  // continue文
  for (int i = 0; i < 5; i++) {
    if (i == 3) {
      continue;
    }
    print("i is $i");
  }
}
実行結果
x is greater than 5
i is 0
i is 1
i is 2
i is 3
i is 4
number is 1
number is 2
number is 3
number is 4
number is 5
count is 0
count is 1
count is 2
count is 3
count is 4
i is 0
i is 1
i is 2
i is 3
i is 4
color is red
i is 0
i is 1
i is 2
i is 4
i is 5
i is 0
i is 1
i is 2
i is 4
i is 5
for文とif文の組合わせ :
main() {
  for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) {
      print("$i は2の倍数です。");
    } else if (i % 3 == 0) {
      print("$i は3の倍数です。");
    } else {
      print("$i は2の倍数でも3の倍数でもありません。");
    }
  }
}
実行結果
0 は2の倍数です。
1 は2の倍数でも3の倍数でもありません。
2 は2の倍数です。
3 は3の倍数です。
4 は2の倍数です。
5 は2の倍数でも3の倍数でもありません。
6 は2の倍数です。
7 は2の倍数でも3の倍数でもありません。
8 は2の倍数です。
9 は3の倍数です。
for文:
  for (int i = 0; i < 10; i++) {
C言語などと共通する三文式for文です。
最初の文は初期設定で変数の宣言もでき、宣言された変数のスコープはfor文が終わるまでです。
二番目の文は反復条件で、値はtrueかfalseである必要があり、trueであるかぎる続く文（内容としましょう）を反復します。
三番目の文は内容を実行したあと毎回評価されます。
if文:
    if (i % 2 == 0) {
      print("$i は2の倍数です。");
    } else if (i % 3 == 0) {
      print("$i は3の倍数です。");
    } else {
      print("$i は2の倍数でも3の倍数でもありません。");
    }
i % 2は、「iを2で割った余り」を表す式で、条件式全体 i % 2 ＝= 0は、「iを2で割った余りが0ならtrueそうでなければfalse」を表す式、、となります。
if文は条件式がtrueであればifに続く文を、（else節があれば）条件式がfalseならばelseに続く文を実行します。
if の条件式に一致せずelseがある場合はelseに続く文を実行しますが、if文も文なので } else if (i % 3 == 0) { のように続けて別の条件を評価することができます他の言語では、elseifやelsifなどの特別な表現を用意している場合がありますが、Dartは「else節に対応する文がif文」と表現します。。
while文とif文の組合わせ :
main() {
  int i = 0;
  while (i < 10) {
    if (i % 2 != 0) {
      print("$i は奇数です。");
    } else {
      print("$i は偶数です。");
    }
    i++;
  }
  print(i);
}
実行結果
0 は偶数です。
1 は奇数です。
2 は偶数です。
3 は奇数です。
4 は偶数です。
5 は奇数です。
6 は偶数です。
7 は奇数です。
8 は偶数です。
9 は奇数です。
10
while文とスコープ:
  print(i);
上のfor文とif文の組合わせをwhileを使ってあらわあすと、概ねこのようになります。
「概ね」と言うのは、変数iのスコープは（while文の外で宣言しているので）main関数末まで続くので、while文を抜けても print(i);は有効です。
for文とswitch文の組合わせ :
main() {
  for (int i = 1; i < 10; i++) {
    switch (i) {
      case 2:
      case 4:
      case 6:
      case 8:
        print("$iは、2の倍数です。");
        break;
      case 3:
      case 6:
      case 9:
        print("$iは、3の倍数です。");
        break;
      case 5:
        print("$iは、5の倍数です。");
        break;
      case 7:
        print("$iは、7の倍数です。");
        break;
      default:
        print("$iは、2,3,5,7のいづれの倍数でもありません。");
    }
  }
}
実行結果
1は、2,3,5,7のいづれの倍数でもありません。
2は、2の倍数です。
3は、3の倍数です。
4は、2の倍数です。
5は、5の倍数です。
6は、2の倍数です。
7は、7の倍数です。
8は、2の倍数です。
9は、3の倍数です。
forEachメソッドは、リストやマップなどのコレクションの各要素に対して指定した処理を実行するためのメソッドです。
forEachメソッド :
void main() {
  List fruits = ["apple", "banana", "orange"];
  
  // リストの各要素に対して、処理を実行する
  fruits.forEach((fruit) {
    print(fruit);
  });
}
上記の例では、List型のfruitsという変数に対してforEachメソッドを呼び出し、fruitsの各要素に対して、引数で指定された無名関数を実行しています。無名関数内では、printメソッドを使用して、各要素をコンソールに出力しています。
また、forEachメソッドは、以下のようにラムダ式を使用して簡略化することもできます。
forEachメソッドとラムダ式の組み合わせ :
void main() {
  List fruits = ["apple", "banana", "orange"];
  
  // リストの各要素に対して、処理を実行する（ラムダ式）
  fruits.forEach((fruit) => print(fruit));
}
上記の例では、引数で指定する無名関数をラムダ式で表しています。これにより、コードの記述量が簡略化されます。
for文とコレクションの組合わせ :
main() {
  const array = [2, 3, 5, 7];
  print(array.runtimeType);
  for (var x in array) {
    print("for-in: $x");
  }
  array.forEach((item) {
    print("forEach: $item");
  });
  const set = {2, 3, 5, 7};
  print(set.runtimeType);
  for (var x in set) {
    print("for-in: $x");
  }
  set.forEach((item) {
    print("forEach: $item");
  });
  const map = {'apple': 'リンゴ', 'orange': 'オレンジ', 'banana': 'バナナ'};
  print(map.runtimeType);
  for (var key in map.keys) {
    print('$key : ${map[key]}');
  }
  for (var value in map.values) {
    print('for in map.values: $value');
  }
  map.forEach((var key, var value) {
    print('forEach: $key : $value');
  });
}
実行結果
JSArray
for-in: 2
for-in: 3
for-in: 5
for-in: 7
forEach: 2
forEach: 3
forEach: 5
forEach: 7
_UnmodifiableSet
for-in: 2
for-in: 3
for-in: 5
for-in: 7
forEach: 2
forEach: 3
forEach: 5
forEach: 7
ConstantStringMap
apple : リンゴ
orange : オレンジ
banana : バナナ
for in map.values: リンゴ
for in map.values: オレンジ
for in map.values: バナナ
forEach: apple : リンゴ
forEach: orange : オレンジ
forEach: banana : バナナ
Dartはオブジェクト指向言語であり、クラスを使ってオブジェクトを定義できます。クラスはフィールドとメソッドを持ち、フィールドはオブジェクトの状態を表し、メソッドはオブジェクトの振る舞いを定義します。
以下は、Dartでクラスを定義し、オブジェクトを作成する例です。例として、Personクラスを定義し、名前と年齢をフィールドに持ち、年齢を1歳増やすメソッドを定義します。また、main関数で2つのPersonオブジェクトを作成し、1つのオブジェクトの年齢を増やして表示します。
クラスの例 :
// Personクラスの定義
class Person {
  String name; // 名前
  int age; // 年齢
  
  // コンストラクタ
  Person(this.name, this.age);
  
  // 年齢を1歳増やすメソッド
  void incrementAge() {
    age++;
  }
}
void main() {
  // Personオブジェクトの作成
  var person1 = Person('Alice', 20);
  var person2 = Person('Bob', 25);
  
  // person1の年齢を1歳増やす
  person1.incrementAge();
  
  // person1とperson2の情報を表示する
  print('Person 1: ${person1.name}(${person1.age})');
  print('Person 2: ${person2.name}(${person2.age})');
}
実行結果
Person 1: Alice(21) 
Person 2: Bob(25)
まず、Personクラスが定義されています。このクラスには、名前と年齢を表すString型のnameフィールドとint型のageフィールドが含まれています。また、このクラスには、名前と年齢を引数として受け取るコンストラクタと、年齢を1歳増やすメソッドincrementAgeが含まれています。
次に、main関数では、2つのPersonオブジェクトを作成し、1つのオブジェクトの年齢を増やしています。varキーワードを使って、Personオブジェクトを作成しています。Person('Alice', 20)は、Personクラスのコンストラクタを呼び出し、nameを'Alice'、ageを20で初期化しています。
その後、person1オブジェクトのincrementAge()メソッドが呼び出され、その結果、person1オブジェクトのageフィールドが1増えます。
最後に、person1とperson2の情報を表示するために、print()関数が使用されます。それぞれのオブジェクトの名前と年齢が表示されます。
継承は、既存のクラスを基にして新しいクラスを定義することができます。
継承の例 :
// Personクラスの定義
class Person {
  String name; // 名前
  int age; // 年齢
  
  // コンストラクタ
  Person(this.name, this.age);
  
  // 年齢を1歳増やすメソッド
  void incrementAge() {
    age++;
  }
}
void main() {
  // Personオブジェクトの作成
  var person1 = Person('Alice', 20);
  var person2 = Person('Bob', 25);
  
  // person1の年齢を1歳増やす
  person1.incrementAge();
  
  // person1とperson2の情報を表示する
  print('Person 1: ${person1.name}(${person1.age})');
  print('Person 2: ${person2.name}(${person2.age})');
}
実行結果
Tama is eating. 
Meow!
この例では、Animalという親クラスが定義され、nameというインスタンス変数とeat()というメソッドが定義されています。そして、Catという子クラスがAnimalを継承しています。Catはnameを引数に取るコンストラクタとmeow()というメソッドを持っています。
main関数では、Catのインスタンスであるcatを作成し、eat()とmeow()メソッドを呼び出しています。eat()メソッドはAnimalクラスから継承されたもので、meow()メソッドはCatクラスで定義されたものです。
コードの理解はプログラミング言語の学習において重要です。
以下は、動作する簡潔なコード例をいくつかご紹介します。
エラトステネスの篩を、若干 Dart らしく書いてみました。
エラトステネスの篩:
void eratosthenes(int n) {
  var sieve = List.filled(n + 1, true);
  for (var i = 2; i < sieve.length; i++) {
    if (!sieve[i]) continue;
    print("$i ");
    for (var j = 2 * i; j < sieve.length; j += i) {
      sieve[j] = false;
    }
  }
}
void main() {
  eratosthenes(100);
}
eratosthenes関数は、指定された整数nまでの素数を見つけるために使用されます。この関数内で、sieveと呼ばれるブール型のリストが作成されます。このリストは、インデックスが数値に対応し、その数が素数であるかどうかを示します。最初はすべての数値が素数であると仮定されます。
forループは、2からsieveの長さまでの範囲で回ります。このループは、素数を探すために各数値を確認します。
if (!sieve[i]) continue;は、sieve[i]がfalse（素数でないことを示す）場合には処理をスキップし、次の数値を調べるためにループを進めます。素数でない数値の倍数は素数ではないため、これによって非素数を除外します。
素数である数値iの倍数を探すための内側のforループがあります。これは、jを2 * iからsieveの長さまでiずつ増やしながらループします。このループでは、sieve[j]をfalseに設定することで、jが素数でないことを示します。
main関数では、eratosthenes関数を引数100で呼び出しています。これにより、100以下のすべての素数が見つけられ、それらがコンソールに出力されます。
エラトステネスの篩は、効率的な方法で素数を見つけるアルゴリズムであり、指定された範囲の素数を比較的高速に見つけることができます。
最大公約数と最小公倍数を、若干 Dart らしく書いてみました。
最大公約数と最小公倍数:
int gcd2(int m, int n) => n == 0 ? m : gcd2(n, m % n);
int gcd(List ints) {
  if (ints.isEmpty) {
    throw ArgumentError("List of integers cannot be empty");
  }
  return ints.reduce(gcd2);
}
int lcm2(int m, int n) => m * n ~/ gcd2(m, n);
int lcm(List ints) {
  if (ints.isEmpty) {
    throw ArgumentError("List of integers cannot be empty");
  }
  return ints.reduce(lcm2);
}
void main() {
  print("gcd2(30, 45) => ${gcd2(30, 45)}");
  print("gcd([30, 72, 12]) => ${gcd([30, 72, 12])}");
  print("lcm2(30, 72) => ${lcm2(30, 72)}");
  print("lcm([30, 42, 72]) => ${lcm([30, 42, 72])}");
}
実行結果:
gcd2(30, 45) => 15
gcd([30, 72, 12]) => 6
lcm2(30, 72) => 360
lcm([30, 42, 72]) => 2520
以下は、Dartコードの解説です。
gcd2関数は、ユークリッドの互除法を使用して整数 m と n の最大公約数（GCD）を再帰的に計算します。
三項演算子 n == 0 ? m : gcd2(n, m % n) を使用して、n が 0 ならば m を、そうでなければ再帰的に gcd2 を呼び出します。
gcd関数は、与えられた整数のリストに対して最大公約数（GCD）を計算します。
リストが空であれば、ArgumentErrorをスローして空のリストは処理できないようにしています。
ints.reduce(gcd2) を使用して、リスト内の各要素に対して gcd2 を繰り返し適用して最終的な GCD を計算します。
lcm2関数は、整数 m と n の最小公倍数（LCM）を計算します。
m * n ~/ gcd2(m, n) を使用して、m と n の積を最大公約数（gcd2）で割ってLCMを計算しています。
lcm関数は、与えられた整数のリストに対して最小公倍数（LCM）を計算します。
リストが空であれば、ArgumentErrorをスローして空のリストは処理できないようにしています。
ints.reduce(lcm2) を使用して、リスト内の各要素に対して lcm2 を繰り返し適用して最終的な LCM を計算します。
main関数は、それぞれの関数を呼び出して結果を出力しています。
文字列補間 (${}) を使用して、関数呼び出しの結果を文字列として表示しています。
二分法を、若干 Dart らしく書いてみました。
二分法:
double bisection(num low_, num high_, double Function(double) f) {
  double low = low_.toDouble();
  double high = high_.toDouble();
  double x = (low + high) / 2;
  double fx = f(x);
  if ((fx.abs()) < 1.0e-10) {
    return x;
  }
  if (fx < 0.0) {
    low = x;
  } else {
    high = x;
  }
  return bisection(low, high, f);
}
void main() {
  print('${bisection(0, 3, (double x) => x - 1)}');
  print('${bisection(0, 3, (double x) => x * x - 1)}');
}
実行結果:
0.9999999999417923
1.0000000000291038
旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法の例を Dart に移植しました。
このDartコードは、二分法を使用して与えられた関数の根（ゼロとなる点）を見つけます。bisection関数は、指定された範囲内で与えられた関数fの根を見つけます。main関数では、いくつかの関数を用いて根を見つけるためにbisection関数を呼び出しています。
Dart: Googleによって開発されたオープンソースのプログラミング言語。
クラス(class): オブジェクト指向プログラミングにおけるデータとそれを操作するメソッドをまとめたもの。
インスタンス(instance): クラスから生成された実体。
継承(inheritance): 親クラスのメソッドや変数を子クラスが引き継いで使用すること。
インターフェイス(interface): クラスが実装しなければならないメソッドや変数の一覧。
ライブラリ(library): Dartのコードをパッケージ化して再利用可能にする仕組み。
静的型付け(static typing): 変数にどのような型の値しか代入できないかを事前に指定すること。Dartは静的型付けに対応している。
ミックスイン(mixin): 複数のクラスに同じメソッドや変数を追加することができる仕組み。
Future: 非同期処理を表すオブジェクト。Dartでは、非同期処理をFutureで表現することが一般的である。
async/await: 非同期処理をシンプルに扱うための仕組み。Dartでは、async/awaitキーワードを使用することで非同期処理を直感的に実装することができる。
Flutter: Dartを使用したGoogleによるモバイルアプリケーションフレームワーク。