Flutterアプリケーション開発概論

戻り値と型設計

この節の目的は、

関数が「何を受け取り、何を返すのか」を、型まで含めて整理して書けるようになること

です。前の 2-5 では、関数と引数を学びました。今回はその続きとして、戻り値の意味、戻り値の型、

void

との違い、そして「どんな型で返すと読みやすく安全か」という型設計の入口を学びます。Dart 公式は、関数が値を返す場合は戻り値型を持ち、関数型や null safety を通して型安全に扱えると説明しています。

歴史的に見ると、「関数が値を返す」という考え方はかなり古く、1956年の FORTRAN Programmer’s Reference Manual には

FUNCTION

SUBROUTINE

が載っており、関数は値を返すまとまり、サブルーチンは処理のまとまりとして区別されていました。さらに 1960 年の ALGOL 60 改訂報告では、

procedure declarations

function designators

が整理され、実引数と仮引数の考え方も明確になっていきました。つまり、戻り値と型設計は、現代の Dart だけの話ではなく、プログラミング言語のかなり深い土台にある考え方です。

1. 戻り値とは何か

戻り値とは、

関数が処理した結果として呼び出し元へ返す値

のことです。たとえば、2つの数の合計、テストの判定結果、自己紹介文、税込み価格などが戻り値になりえます。2-5 では関数を「処理に名前をつける仕組み」と学びましたが、戻り値があると、その処理結果を次の処理へつなげられるようになります。

たとえば、ただ表示するだけの関数と、値を返す関数を比べると違いが見えます。

void sayHello() {
  print('こんにちは');
}

String makeHello() {
  return 'こんにちは';
}

void main() {
  sayHello(); // その場で表示する
  String message = makeHello(); // 戻り値を受け取る
  print(message);
}

sayHello() は表示して終わりです。makeHello() は文字列を返すので、その結果を変数へ入れたり、別の関数へ渡したりできます。ここが戻り値の大きな価値です。

2. voidと「値を返す関数」の違い

Dart では、戻り値がない関数には

void

を使います。

void

は、

この関数は値を返さない

という意味です。一方で、

int
String
bool

などの型を書いた関数は、その型の値を返します。Dart 公式でも、関数は戻り値型を持ち、

void

は「返す値がない」ことを表す型として扱われています。

次の例を見てください。

void showSchoolName() {
  print('Prince Academy');
}

String getSchoolName() {
  return 'Prince Academy';
}

void main() {
  showSchoolName();

  String schoolName = getSchoolName();
  print(schoolName.length);
}

showSchoolName() はその場で表示して終わりです。getSchoolName() は文字列を返すので、あとで長さを調べたり、別の文章へつないだりできます。初心者が最初に持つと良い感覚は、「表示する関数」と「値を作って返す関数」は役割が違うということです。

3. Dart では戻り値型をどう書くのか

Dart の関数は、基本的に次の形で書きます。

戻り値の型 関数名(引数) {
  処理
}

たとえば、2つの整数の合計を返すならこう書きます。

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

void main() {
  int result = add(3, 4);
  print(result);
}

ここで

int add(...)

と書いているので、「この関数は最終的に整数を返す」と分かります。Dart の built-in types 組み込み型には

int
double
String
bool

などがあり、これらは戻り値型にもよく使われます。

型を書くことの意味は、単に見た目をきれいにすることではありません。読み手に意図を伝え、間違いを減らすためです。たとえば「点数を返すなら int だろう」「合否を返すなら bool だろう」という見通しが立つので、コードが読みやすくなります。

4. returnとは何か

return

は、関数の結果として値を返し、その時点で関数を終える命令です。Dart 公式の関数説明でも、関数が値を返すときは

return

を使うのが基本です。

たとえば、点数から合否を返す関数はこう書けます。

bool isPassing(int score) {
  return score >= 60;
}

void main() {
  print(isPassing(75));
  print(isPassing(40));
}

この関数は、score >= 60 の結果、つまり truefalse をそのまま返しています。ここで大切なのは、戻り値は「最終的にほしい結果」だけを返すということです。途中の処理全部を外へ見せる必要はありません。

5. 型設計とは何か

ここで「型設計」という少し大きな言葉が出てきます。型設計とは、

その関数がどんな種類の値を返すべきかを、目的に合わせて決めること

です。初心者のうちは「動けばよい」と感じやすいですが、型設計が良いと、コードはかなり読みやすく、安全になります。Dart の type system は sound な方向へ強化されてきており、Dart 2 以降は特に型の意味が重くなっています。

たとえば、テストの点数から「合格かどうか」を返したいとき、次の二つを比べてみます。

String checkResultAsText(int score) {
  if (score >= 60) {
    return '合格';
  } else {
    return '不合格';
  }
}
bool isPassing(int score) {
  return score >= 60;
}

どちらも使えますが、「合格かどうか」だけ知りたいなら bool のほうが設計として自然です。文字列だと表示用には便利ですが、あとで条件分岐に使いたいときは bool のほうが扱いやすいです。つまり、型設計は「将来その値をどう使うか」まで含めて考えることです。

6. 文字列で返すか、真偽値で返すか、数値で返すか

初心者がよく迷うのが、「結局どの型で返せばいいのか」です。ここでは三つの基本を押さえます。

6-1. 真偽値 boolが向く場面

Yes / No、できる / できない、合格 / 不合格のように、二択で答えたいときです。

bool isAdult(int age) {
  return age >= 18;
}

6-2. 数値 int / double が向く場面

合計、平均、個数、金額、割合など、計算結果として使いたいときです。

double calcAverage(int a, int b) {
  return (a + b) / 2;
}

6-3. 文字列 String が向く場面

表示用メッセージ、タイトル、説明文など、人に見せる形でほしいときです。

String makeGreeting(String name) {
  return 'こんにちは、$name さん';
}

ここでのコツは、人へ見せたいなら String、判断に使いたいなら bool、計算したいなら数値と考えることです。

7. null safety と戻り値

Dart は sound null safety を持つので、戻り値でも

null

をどう扱うかをはっきりさせる必要があります。Dart 公式は、型はデフォルトで non-nullable であり、nullable にしたい場合は

?

を付けると説明しています。

たとえば、「生徒番号からニックネームを探すが、見つからないかもしれない」なら、戻り値を String? にすることがあります。

String? findNickname(int studentId) {
  if (studentId == 1) {
    return 'Aoi';
  }
  return null;
}

void main() {
  String? nickname = findNickname(2);

  if (nickname != null) {
    print(nickname.length);
  } else {
    print('ニックネームは未登録です');
  }
}

ここで重要なのは、「見つからない可能性」を型へ表すことです。String と書いてしまうと、必ず文字列が返る印象になります。String? と書けば、「null の可能性があるから確認してね」とコード自体が伝えてくれます。これが null safety の強みです。

8. 型推論と戻り値

Dart では、関数の戻り値型を省略できる場面もありますが、初心者のうちは

関数の戻り値型はなるべく明示する

ほうが理解しやすいです。Effective Dart では、ローカル関数や無名関数などで型推論が働くことが説明されていますが、公開 API や学習段階では明示のほうが読みやすさを保ちやすいです。

たとえば次のように書けます。

int square(int x) {
  return x * x;
}

このコードでは、square が整数を返すことが一目で分かります。もし戻り値型を書かなければ、読む人は本文を見て推測しなければなりません。学習中は、型は説明でもあると考えるとよいです。

9. Function 型と「関数も値である」という考え方

Dart 公式は、関数は first-class objects、つまり

関数そのものを値として扱える

と説明しています。これは少し先の話ですが、戻り値と型設計を学ぶうえで知っておくと面白い考え方です。

たとえば、関数を変数へ入れることができます。

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

void main() {
  int Function(int, int) calc = add;
  print(calc(2, 3));
}

この int Function(int, int) は、「整数を2つ受け取り、整数を返す関数」という型です。今すぐ使いこなせなくても大丈夫ですが、関数にも型があるという感覚は、Dart を学ぶうえでとても大切です。

10. 歴史をもう少しだけ整理する

1956 年の FORTRAN で

FUNCTION

SUBROUTINE

が現れたことは、処理を部品化し、その結果を返すという考え方を実用化した大きな一歩でした。そこでは、関数は数式のように値を返すまとまりとして設計されました。

1960 年の ALGOL 60 では、手続きと引数の考え方がさらに整理され、formal parameters 仮引数 と actual parameters 実引数 の関係が明確になりました。これにより、「値を受け取り、値を返す」という今の関数の骨格がかなりはっきりしました。

現代の Dart は、その流れを受けつつ、Dart 2 でより sound な static type system を強め、Dart 2.12 で sound null safety を導入し、Dart 3 では 100% sound null safety の基盤の上で records や patterns を広げています。つまり、戻り値と型設計は、歴史的には古く、言語機能としては今も進化し続けているテーマです。

11. 実践

実践1: 合計点を返す

int totalScore(int a, int b, int c) {
  return a + b + c;
}

void main() {
  int total = totalScore(80, 75, 90);
  print(total);
}

実践2: 合格かどうかを返す

bool isPassing(int score) {
  return score >= 60;
}

void main() {
  print(isPassing(72));
  print(isPassing(45));
}

実践3: 表示用のコメントを返す

String makeComment(int score) {
  if (score >= 90) {
    return 'とても良いです';
  } else if (score >= 60) {
    return '合格です';
  } else {
    return '再提出です';
  }
}

void main() {
  print(makeComment(88));
}

実践4: 見つからないかもしれない値を返す

String? findClassroom(int grade) {
  if (grade == 1) {
    return '1-A';
  }
  return null;
}

void main() {
  String? room = findClassroom(2);

  if (room != null) {
    print(room);
  } else {
    print('教室情報がありません');
  }
}

この4つを見比べると、戻り値の型は「返したいものの性質」に合わせて変えるべきだと分かります。

12. 練習問題

問題1

戻り値とは何ですか。初心者向けに一文で説明してください。

問題2

次のコードは何を表示しますか。

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

void main() {
  print(add(2, 5));
}

問題3

次の関数の戻り値型として最も自然なのは何ですか。

「年齢を受け取り、成人かどうかを返す関数」

  1. String
  2. bool
  3. double

問題4

次のコードは何を表示しますか。

String message() {
  return 'Hello';
}

void main() {
  String text = message();
  print(text.length);
}

問題5

次の関数で ? が必要な理由を説明してください。

String? findNickname(int id) {
  if (id == 1) {
    return 'Rin';
  }
  return null;
}

問題6

次の二つの関数の違いを説明してください。

void showResult() {
  print('合格');
}

String getResult() {
  return '合格';
}

問題7

「税込み価格を返す関数」を作るとしたら、戻り値型は何が自然そうですか。理由も一言で書いてください。

13. 練習問題の答え

答え1

例です。

戻り値とは、関数が処理した結果として呼び出し元へ返す値です。

答え2

7 を表示します。

答え3

正解は **2. **bool です。 成人かどうかは、はい / いいえ の二択なので bool が自然です。

答え4

5 を表示します。'Hello' の長さは 5 文字だからです。

答え5

見つからない場合に null を返す可能性があるからです。

Dart の null safety では、

null

を返す可能性があるなら nullable 型、つまり

String?

のように書く必要があります。

答え6

showResult() は表示するだけで値を返しません。getResult() は文字列 '合格' を返すので、変数へ入れたり別の処理へ渡したりできます。

答え7

double が自然そうです。

税込み価格は小数を含む可能性があるからです。

14. まとめ

この節では、戻り値と型設計の基本を学びました。戻り値は、関数が処理結果として呼び出し元へ返す値です。

void

は返す値がないことを表し、

int
double
String
bool

などは返す値の種類を表します。型設計では、「人に見せたいのか」「判定に使いたいのか」「計算に使いたいのか」を考えて、自然な型を選ぶことが大切です。Dart は sound な type system と null safety を持つため、戻り値にも「null の可能性があるか」を型で表せます。

歴史的には、FORTRAN の FUNCTION、ALGOL 60 の procedures と parameters が、戻り値と型の考え方を早い時期から支えてきました。現代の Dart は、その長い流れの上に、型安全性と null safety を強くのせた言語です。だから、戻り値と型設計を学ぶことは、単に文法を覚えるだけでなく、読みやすく、壊れにくいコードを書く感覚を育てることでもあります。次節では、ここまで学んだ内容を使って、基本プログラム作成の実践へ進みます。

参考文献

教材トップへ戻る