BMI を計算するメソッド

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

BMI を計算するメソッド とは

体重と身長を引数で受け取り、BMI を double で返すメソッドを書きながら、「計算公式を 1 ヶ所に集約する」というメソッドの本質的なご利益を体に馴染ませる。

計算メソッドを「公式の置き場所」として書く

前のレッスンで、メソッドの基本構文 public static 戻り値の型 メソッド名(引数の型 引数名) を学びました。int を 2 つ受け取って合計を返す add(int a, int b)、税抜き価格から税込みを返す taxIncluded(int price) といった例で、「メソッドは値を入れたら値が返ってくる箱」というイメージは持てたと思います。

このレッスンでは、もう一歩踏み込んで、メソッドを 「計算公式の置き場所」 として書く感覚を身につけます。題材は、健康診断や保健の授業で誰もが聞いたことのある BMI (Body Mass Index、体格指数) です。体重と身長から「太り具合の目安」を出す指標で、世界中で使われています。

BMI は次の公式で計算します。体重(kg) ÷ (身長(m) × 身長(m))。たとえば体重 60 kg、身長 1.7 m の人なら、60 / (1.7 * 1.7) ≒ 20.76 です。BMI 値が 18.5 未満なら痩せ気味、18.5 〜 25 なら標準、25 以上は肥満気味とされています。

この公式を「メソッドにしない」と何が困るか

仮にこの計算を、メソッドにせずアプリのあちこちでベタ書きしたとします。健診結果の表示画面でも、医師向けレポートでも、家族の体重管理ページでも、毎回 weight / (height * height) と書く感じです。

見た目は短いので「別に問題なくない?」と思うかもしれませんが、次のような場面で一気に困ります。

  • 海外向けに、身長を cm で受け取って中で m に直すバージョンが必要になった
  • BMI の計算式を改良した新版 (例: 子供向けの補正式) に差し替えたくなった
  • 引数の名前を weight から weightKg に変えて、誰が読んでも単位が分かるようにしたくなった

メソッドにしておけば、こうした変更はすべて メソッドの中身を書き換えるだけ で済みます。ベタ書きしていると、アプリ全体から weight / (height * height) を grep して、1 つずつ直す羽目になります。1 ヶ所書き忘れた瞬間にバグです。

ソフトウェアの世界には DRY (Don't Repeat Yourself) という有名な原則があります。「同じ知識をコードの中で 2 度書かない」という考え方で、メソッドはこれを実現するもっとも基本的な道具です。BMI の公式という「知識」を、メソッドの中の 1 行だけに閉じ込めるのが今回のゴールです。

メソッドのシグネチャを設計する

まずは「外から見たメソッドの形」、いわゆる シグネチャ を決めます。シグネチャとは、メソッド名・戻り値の型・引数の型と並び順のことです。中身を書く前にこれを決めておくと、頭が整理されてバグも減ります。

今回の bmi メソッドのシグネチャは下記のとおりです。

Java

public static double bmi(double weight, double height)
  • public static — クラスをインスタンス化せずに Solution.bmi(...) で呼べる定番の組み合わせ
  • double (戻り値) — BMI20.76 のような小数を持つので int ではなく double
  • bmi — メソッド名は小文字始まりのキャメルケース。3 文字でも意味が伝わるので無理に長くしない
  • double weight, double height — 引数も両方とも小数を扱えるよう double にする

ここで「身長は intcm のままでもいいのでは?」と思うかもしれません。確かに int でも書けますが、その場合 メソッドの内側で 100 で割って m に変換 する責任が発生します。「外側 (呼び出し元) と内側 (メソッド) のどちらが単位変換を持つか」は設計の大事な選択です。今回は メソッドはあくまで純粋な BMI の公式だけを担当する という方針にして、単位変換は呼び出す側に任せます。これは「メソッドを小さく保つ」「責任を 1 つに絞る」というプロの常識的な発想です。

よい関数 / メソッドは、単一責任の原則 (Single Responsibility Principle) に沿って書かれます。「1 つの仕事だけをする」「副作用がない (画面表示や DB 書き込みをしない)」「同じ入力なら必ず同じ出力を返す」と、テストもデバッグもぐっと楽になります。bmi(weight, height) はまさにこの典型例で、return weight / (height * height); の 1 行で終わる予定です。

公式をコードに翻訳する

シグネチャが決まれば、中身は驚くほどあっさり書けます。

Java

public class Solution { public static double bmi(double weight, double height) { return weight / (height * height); } }

たった 1 行。これだけで BMI の計算メソッドが完成します。weight / (height * height) という公式が、コードに 1 ヶ所だけ存在している状態を作れました。

Java の四則演算には演算子の優先順位があって、* (掛け算) は / (割り算) と同じ強さです。weight / height * height のように丸カッコを省くと、左から順に処理されて (weight / height) * height = weight になってしまい、まったく違う計算結果になります。下記のように 必ず (height * height) を丸カッコで囲む ことが重要です。

Java

// NG: 左から計算されて weight に戻ってしまう return weight / height * height; // OK: 丸カッコで「先に二乗してから割る」と明示する return weight / (height * height);

別解として、Math.pow(height, 2) を使う書き方もあります。下記のとおりです。

Java

public class Solution { public static double bmi(double weight, double height) { return weight / Math.pow(height, 2); } }

Math.pow(a, b) は「a の b 乗」を double で返す Java 標準のメソッドです。二乗くらいなら height * height と書く方が速くて読みやすいですが、3 乗や 2.5 乗が必要な複雑な公式では Math.pow の出番です。今回はどちらでも構いません。

データの流れを図で追う

bmi(60.0, 1.7) を例に、入力 → 計算 → 出力の流れを下記の図で確認します。

diagram (will load when visible)

図にすると当たり前のことを書いているように見えますが、メソッドの中身を考えるときは常にこの「入力 → 加工 → 出力」の 3 段階を頭に描くと迷子になりにくいです。複雑なメソッドほど、紙やホワイトボードに同じ図を書いてから実装すると、バグがぐっと減ります。

テストしやすい設計とは

もう 1 つだけ、設計の話をさせてください。メソッドが テストしやすい とはどういう状態かというと、同じ引数を渡せば必ず同じ戻り値が返ってくる という性質を持っていることです。これを 純粋関数 (pure function) 的な書き方とも呼びます。

下記のように、画面表示や時刻取得が混ざったメソッドは、テストが一気に難しくなります。

Java

// 悪い例: BMI 計算と画面表示が混ざっている public static void printBmi(double weight, double height) { System.out.println("あなたの BMI は " + (weight / (height * height)) + " です"); }

これだと、自動テストから値だけを取り出せず、System.out をキャプチャするような大掛かりな仕組みが必要になります。一方、今回のように 値を返すだけ のメソッドなら、assertEquals(15.0, Solution.bmi(60.0, 2.0)) のように 1 行で書けます。

「値を計算するメソッド」と「結果を表示するメソッド」は分けて作るのが鉄則です。今回作る bmi は前者、画面に出すのは別のメソッド (たとえば printResult) の仕事、というふうに役割を分けると、後から BMI の計算式だけ差し替えるのも、表示形式だけ変えるのも、片方に閉じて作業できます。

よくある間違い

手を動かす前に、BMI 計算でほぼ全員が一度はやらかす落とし穴を 3 つ紹介します。

1 つ目は、身長を cm のまま計算してしまう ミスです。bmi(60.0, 170.0) のように身長 170 を cm のつもりで渡すと、60 / (170 * 170) = 60 / 28900 ≒ 0.00207 という、現実離れした値が返ってきます。今回のメソッドは仕様として「身長は m 単位」と決めたので、呼び出す側で 170 / 100.0 = 1.7 のように事前に変換してから渡す責任があります。テストが通らないときは、まず単位を疑ってください。

2 つ目は、身長を 2 乗するのを忘れる ミスです。weight / height だと、ただの「kg ÷ m」になってしまい、まったく別の指標になります。BMI は身長の 2 乗 で割るのが定義なので、height * heightMath.pow(height, 2) を必ず入れます。「公式の見た目」と「コードの見た目」が一致しているかを、書いた後に必ず指差し確認しましょう。

3 つ目は、整数除算してしまう 罠です。仮にメソッドのシグネチャを int weight, int height で書いていて、メソッド内部で weight / (height * height) と計算すると、int 同士の割り算なので 整数除算 になり、小数点以下が切り捨てられてしまいます。60 / (2 * 2) = 60 / 4 = 15 のように偶然うまくいく値もありますが、60 / (1 * 1) = 60 のような感じで、本当は 60.0 を期待しているのに小数情報が失われます。今回のメソッドは引数を double にしているので大丈夫ですが、もし int で書きたい場面では (double) weight / (height * height) のようにキャストが必要です。

余談として、4 つ目のミスとして「掛け算と割り算の順序」も挙げておきます。weight / height * height (カッコなし) は、weightheight で割ってから height を掛けるので、結果は weight そのものになってしまいます。さきほども触れましたが、初心者がもっとも頻繁に踏む地雷なので、しつこいくらい強調しておきます。

やってみよう

それでは課題に挑戦しましょう。下記の手順で進めると詰まりにくいです。

  1. 右側のエディタで starter_code を開く
  2. return 0.0; を消して、return weight / (height * height); に書き換える
  3. 実行ボタンを押して、すべてのテストが緑になることを確認する

うまくいったら、下記のような書き換えを試して、メソッド設計の感覚を広げてみましょう。

  • Math.pow(height, 2) を使う版に書き換えて、結果が同じになることを確かめる
  • 一度ローカル変数 double squared = height * height; を作ってから return weight / squared; と 2 行に分けてみる
  • 試しに weight / height * height (カッコなし) と書き換えて、テストがどう失敗するか観察する
  • 体重 60.0 身長 1.7 を入れたとき、エディタに Solution.bmi(60.0, 1.7) の呼び出し例を書いて結果を眺める

この bmi メソッドはたった 1 行ですが、ここに詰め込まれている考え方 (公式を 1 ヶ所にまとめる、単位を引数で明確にする、副作用を作らない、テストしやすく値を返す) は、これから先に書くあらゆる業務ロジックで何度も顔を出します。「メソッドは小さく、責任を 1 つに、計算結果は return で返す」を合言葉に、次のレッスンでも一緒に組み立てていきましょう。

よくある質問

Q. Java と他言語の文法はどう違いますか?

A. Java は静的型付けで、変数宣言時に型を明示します(int x = 0;)。中括弧でブロックを表し、文末にセミコロンが必要です。Python の動的型付けや JavaScript の柔軟さに慣れていると最初は窮屈ですが、コンパイル時にバグが発見できる安全性がメリットです。

Q. コードが動かないときに最初に見るべき場所は?

A. コンパイルエラーは行番号とエラー種別(cannot find symbol、incompatible types など)が表示されます。実行時例外はスタックトレースの最初の at ... が原因行です。IDE(IntelliJ / VS Code)の警告も丁寧に潰すと、半分のバグは未然に防げます。

Q. Java の習得後に学ぶべき技術は何ですか?

A. 基本構文を抑えたら java-intermediate(コレクション / ジェネリクス / Stream / 例外)に進み、Spring Boot や Web 開発に展開するのが王道です。クラウド時代は Kotlin / Scala への展開も視野に入りますが、まずは Java 標準を固めるのが効率的です。

次のレッスン

次は 第 5 章 まとめクイズ で、第 5 章 まとめクイズ を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. BMI の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. BMI とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. Solution クラスに public static double bmi(double weight, double height) を実装する
  2. BMI = 体重 / (身長 * 身長) の公式を使って計算する
  3. 戻り値は double 型。身長は m 単位で渡される前提にする (cm 変換はメソッド内では行わない)

入出力例

test-cases.txt

bmi(60, 2)15 bmi(80, 2)20 bmi(50, 1)50 bmi(100, 2)25 bmi(18, 1)18

ヒント

main.java
main.java
学習モード

メモ

BMI を計算するメソッド

⌘S で保存