Java | 基礎文法:例題をコードに落とす練習

Java Java
スポンサーリンク

コードに落とす練習の全体像

「例題をコードに落とす」とは、文章で書かれた問題を、入力・出力・処理手順・境界条件に分解して、テスト可能な小さな関数やプログラムに組み立てることです。重要なのは「考える順序」を固定すること。問題を読む→入力と出力を決める→制約と境界を洗い出す→手順(擬似コード)を書く→まず動く最小を作る→テスト→リファクタのサイクルで、正確さと読みやすさを両立させます。


問題分解の型(重要ポイントの深掘り)

入力・出力を先に固定する

コードは「関数(入力→出力)」の形で考えると壊れにくくなります。入出力が明確だと、テストが容易になり、内部実装の自由度が上がります。

// 例:文字列を受けて、単語数を返す
static int countWords(String text) { /* 実装 */ }
Java

制約と境界条件を列挙する

「空文字」「null」「重複」「大文字小文字」「極端なサイズ」「外部仕様(文字コード・ロケール)」などを先取りし、仕様を言語化します。これがバグ予防線になります。

// 仕様例:空白は複数でも区切り、空文字は0、nullはIllegalArgumentException
Java

擬似コードで「順序」を決める

いきなりコードにしない。箇条書きの手順(擬似コード)を作ると、実装に迷いがなくなります。

1. nullチェック
2. 先頭末尾の空白を削る
3. 連続空白を1つに正規化
4. 空なら0、そうでなければ分割して長さ

練習 1: FizzBuzz(仕様をコードへ)

仕様と分解

1から100までの整数を順に出力。3の倍数なら「Fizz」、5の倍数なら「Buzz」、両方なら「FizzBuzz」、それ以外は数値。

実装

public class FizzBuzz {
    public static void main(String[] args) {
        for (int i = 1; i <= 100; i++) {
            System.out.println(label(i));
        }
    }
    static String label(int n) {
        boolean fizz = n % 3 == 0;
        boolean buzz = n % 5 == 0;
        if (fizz && buzz) return "FizzBuzz";
        if (fizz) return "Fizz";
        if (buzz) return "Buzz";
        return Integer.toString(n);
    }
}
Java

重要ポイント

分岐の優先順位を「両方→片方→通常」に固定。判定を変数へ落とすと読みやすく、テストしやすくなります。


練習 2: 単語数カウント(入力正規化の設計)

仕様と分解

文字列中の単語数を数える。空白で分割。空文字は0。nullは例外。複数空白や改行は区切りとして扱う。

実装

public final class WordCounter {
    public static int countWords(String text) {
        if (text == null) throw new IllegalArgumentException("text must not be null");
        String trimmed = text.trim();
        if (trimmed.isEmpty()) return 0;
        String normalized = trimmed.replaceAll("\\s+", " ");
        return normalized.split(" ").length;
    }
}
Java

重要ポイント

前処理(trim、連続空白の正規化)で仕様を「単純な分割」に落とす。例外方針を明文化すると、呼び手の責務が明確になります。


練習 3: 配列の中央値(境界とアルゴリズム)

仕様と分解

整数配列の中央値を返す。要素数が奇数なら中央、偶数なら2つの平均(切り捨てでなく四捨五入)。空配列は例外。

実装

import java.util.Arrays;

public final class Median {
    public static double median(int[] xs) {
        if (xs == null || xs.length == 0) throw new IllegalArgumentException("empty");
        int[] a = Arrays.copyOf(xs, xs.length);
        Arrays.sort(a);
        int n = a.length;
        if (n % 2 == 1) return a[n / 2];
        int left = a[n / 2 - 1], right = a[n / 2];
        return (left + right) / 2.0; // 小数で返す(四捨五入不要)
    }
}
Java

重要ポイント

元配列を破壊しないためにコピーしてからソート。偶数ケースの平均は double で返すと誤差や丸めの曖昧さを避けられます。


練習 4: コマンドライン集計(入力検証と終了コード)

仕様と分解

引数で整数を受け取り合計を出力。不正入力があればエラーメッセージを出して失敗終了。引数なしはヘルプ。

実装

public class SumApp {
    public static void main(String[] args) {
        if (args.length == 0) {
            System.err.println("usage: SumApp <ints...>");
            System.exit(1);
        }
        try {
            int sum = java.util.Arrays.stream(args).mapToInt(Integer::parseInt).sum();
            System.out.println(sum);
            System.exit(0);
        } catch (NumberFormatException e) {
            System.err.println("invalid number: " + e.getMessage());
            System.exit(2);
        }
    }
}
Java

重要ポイント

「成功=0、入力エラー=1〜2」など終了コードの契約を決める。標準出力と標準エラーを使い分けると、運用が明快になります。


練習 5: ファイルの行数カウント(リソースと例外)

仕様と分解

UTF-8 のテキストファイルの行数を出力。ファイルがない・読めない場合は理由を表示して失敗終了。

実装

import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.*;

public class LineCount {
    public static void main(String[] args) {
        if (args.length < 1) {
            System.err.println("usage: LineCount <path>");
            System.exit(1);
        }
        var path = Path.of(args[0]);
        try (var br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
            long count = 0;
            while (br.readLine() != null) count++;
            System.out.println(count);
            System.exit(0);
        } catch (IOException e) {
            System.err.println("read error: " + e.getMessage());
            System.exit(3);
        }
    }
}
Java

重要ポイント

文字コードは必ず指定(UTF-8)。try-with-resources で確実にクローズ。例外をユーザー向けメッセージ+終了コードに変換します。


練習 6: 仕様から関数へ(メールバリデーションの最小版)

仕様と分解

メールアドレスの「最低限」チェック。空白なし、1つだけ @ を含む、@ の前後が空でない。細かい RFC 準拠は不要。

実装

public final class EmailValidator {
    public static boolean isSimpleValid(String s) {
        if (s == null) return false;
        String x = s.trim();
        if (x.isEmpty() || x.contains(" ")) return false;
        int at = x.indexOf('@');
        if (at <= 0 || at != x.lastIndexOf('@')) return false;
        if (at == x.length() - 1) return false;
        return true;
    }
}
Java

重要ポイント

「最低限の仕様」を明示して過剰な複雑性を避ける。要件が増えたら段階的に拡張し、テストを追加していきます。


小さく作って磨くサイクル

最小の正しさを先に

すべての境界を一気に詰めるより、「最小仕様を満たす」版を先に動かし、次に境界・性能を足していきます。早いフィードバックが最強の武器です。

テストを伴走させる

問題の例と境界に対応するテストを作り、毎回通ることを確認しながら拡張します。関数化した入出力はテストしやすく、失敗の位置特定も容易です。

static void test() {
    assert WordCounter.countWords("a b  c") == 3;
    assert EmailValidator.isSimpleValid("a@b");
    assert !EmailValidator.isSimpleValid(" @b");
}
Java

リファクタで読みやすさを上げる

重複・長すぎるメソッド・曖昧な命名を直し、例外方針やコメント(なぜ)を整えます。「最初に完璧」は不要、磨き続ける習慣が品質を作ります。


仕上げのアドバイス(重要部分のまとめ)

入出力を先に固定し、制約と境界を言語化、擬似コードで順序を決め、最小で動かしてからテストとリファクタを回す——この型を身体に入れると、どんな例題でも迷わずコードに落とせます。分岐は優先順位を明確に、前処理で仕様を単純化、外部I/Oは文字コード指定、失敗はメッセージ+終了コードに変換。小さな関数へ分割して、テスト可能な形で進めるのが最短です。

タイトルとURLをコピーしました