BMI を計算するメソッド
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(戻り値) —BMIは20.76のような小数を持つのでintではなくdoublebmi— メソッド名は小文字始まりのキャメルケース。3 文字でも意味が伝わるので無理に長くしないdouble weight, double height— 引数も両方とも小数を扱えるようdoubleにする
ここで「身長は int で cm のままでもいいのでは?」と思うかもしれません。確かに 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) を例に、入力 → 計算 → 出力の流れを下記の図で確認します。
図にすると当たり前のことを書いているように見えますが、メソッドの中身を考えるときは常にこの「入力 → 加工 → 出力」の 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 * height か Math.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(カッコなし) は、weightをheightで割ってからheightを掛けるので、結果はweightそのものになってしまいます。さきほども触れましたが、初心者がもっとも頻繁に踏む地雷なので、しつこいくらい強調しておきます。
やってみよう
それでは課題に挑戦しましょう。下記の手順で進めると詰まりにくいです。
- 右側のエディタで
starter_codeを開く return 0.0;を消して、return weight / (height * height);に書き換える- 実行ボタンを押して、すべてのテストが緑になることを確認する
うまくいったら、下記のような書き換えを試して、メソッド設計の感覚を広げてみましょう。
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 つができることを確認しましょう。
- BMI の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. BMI とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- Solution クラスに public static double bmi(double weight, double height) を実装する
- BMI = 体重 / (身長 * 身長) の公式を使って計算する
- 戻り値は 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