Java | オブジェクト指向:メソッド(振る舞い)

Java Java
スポンサーリンク

メソッドの全体像

メソッド(振る舞い)は、オブジェクトが「何ができるか」を表す動作の定義です。フィールド(状態)に対して、検証しながら安全に操作したり、結果を計算して返したりします。Java では「インスタンスメソッド(個体に紐づく)」「static メソッド(クラスに紐づく)」を使い分け、カプセル化(中身を隠す)と不変性(壊れない設計)を守ることで、読みやすく拡張しやすいコードになります。


メソッド定義の基本構文

シグネチャと可視性

public final class User {
    private String name;

    // 戻り値ありのメソッド
    public String name() {                // public: 外から呼べる
        return name;                      // 戻り値型: String
    }

    // 引数あり・戻り値なし(副作用あり)
    public void rename(String newName) {
        if (newName == null || newName.isBlank()) return; // 検証(入力ガード)
        this.name = newName.trim();
    }

    // 内部だけで使う共通処理
    private static String normalize(String s) {
        return s == null ? "" : s.trim().replaceAll("\\s+", " ");
    }
}
Java
  • 可視性: public(外部公開)、private(内部限定)。公開最小が基本です。
  • 戻り値: 計算結果や状態を返すなら値型、変更だけなら void。戻り値で失敗を表すより、例外か Optional の方が明確なことが多いです。
  • 引数: 仕様を満たすために必要最小限。null 許容かどうかを決め、検証を入れます。

this とフィールド操作

public final class Point {
    private int x, y;
    public void move(int dx, int dy) {
        this.x += dx;   // this は「そのオブジェクト自身」
        this.y += dy;
    }
}
Java
  • this: フィールド名と引数名が同じでも、this.x で曖昧さが消えます。

インスタンスメソッドと static メソッド

インスタンスメソッド(個体の振る舞い)

オブジェクトの状態(フィールド)を使う動作はインスタンスメソッドにします。

public final class Rectangle {
    private final int w, h;
    public Rectangle(int w, int h) { this.w = w; this.h = h; }
    public int area() { return w * h; }          // 個体の状態を使う
}
Java

static メソッド(共通のユーティリティ)

個体に依存しない計算は static にすると明確です。副作用のない純粋関数に寄せると再利用しやすくなります。

public final class MathUtil {
    private MathUtil() {}
    public static int clamp(int x, int min, int max) {
        return Math.max(min, Math.min(max, x));
    }
}
Java
  • 選び方: 個体の状態を使うならインスタンス、使わないなら static。混同しないことが可読性の鍵です。

カプセル化と検証(重要ポイントの深掘り)

「直接代入させない」代わりに「検証付きメソッド」を用意する

public final class BankAccount {
    private int balance;

    public BankAccount(int initial) {
        if (initial < 0) throw new IllegalArgumentException("negative");
        this.balance = initial;
    }
    public int balance() { return balance; }

    public void deposit(int amount) {
        if (amount <= 0) throw new IllegalArgumentException("amount>0");
        balance += amount;
    }

    public boolean withdraw(int amount) {
        if (amount <= 0 || amount > balance) return false; // 仕様に応じて例外か戻り値か
        balance -= amount;
        return true;
    }
}
Java
  • 意図: フィールドを private にし、整合性を壊す変更を通さない。
  • 指針: 入力の検証は入り口(メソッド)で。メソッドの契約(受け入れる値・失敗の扱い)を明文化します。

副作用と戻り値の設計

  • 副作用のあるメソッド: void で「変える」。変更は必ず検証付き。
  • 副作用のないメソッド: 値を返す「純粋関数」。テスト・再利用に強い。
public final class Price {
    private final int value;
    public Price(int value) { if (value < 0) throw new IllegalArgumentException(); this.value = value; }

    // 副作用なし:新しい値を返す(不変を好む)
    public Price addTax(double rate) {
        int taxed = (int) Math.round(value * (1 + rate));
        return new Price(taxed);
    }
}
Java

オーバーロードとオーバーライド(多態性の入口)

オーバーロード(同名・引数違い)

状況に応じた呼び分けができる反面、曖昧さには注意します。

public final class Logger {
    public void log(String msg) { System.out.println(msg); }
    public void log(String fmt, Object... args) { System.out.printf(fmt + "%n", args); }
}
Java
  • 指針: 引数の最低数を必須にし、追加分を varargs にまとめると分かりやすい。

オーバーライド(親の振る舞いを型ごとに実装)

同じメソッド名でも、型ごとに「らしい」動作に切り替わります。

public abstract class Shape {
    public abstract double area();
}
public final class Circle extends Shape {
    private final double r;
    public Circle(double r) { this.r = r; }
    @Override public double area() { return Math.PI * r * r; }
}
public final class Rect extends Shape {
    private final double w, h;
    public Rect(int w, int h) { this.w = w; this.h = h; }
    @Override public double area() { return w * h; }
}

Shape s = new Circle(10);
System.out.println(s.area()); // Circle版が呼ばれる
Java
  • @Override: 上書きの意図を明示して、ミスを防ぎます。
  • 利点: 分岐を型へ追い出し、拡張は“型を増やす”で済む。

引数・戻り値の設計とエラー処理

null 許容と Optional

  • 方針: 受け取る側・返す側のどちらが null を扱うかを決め、契約にする。
  • Optional: 「値がない」可能性を型で表現し、null を避けられます。
import java.util.Optional;

public final class Repo {
    public Optional<String> findName(String id) {
        // 見つかったら Optional.of(name)、なければ Optional.empty()
        return id.equals("U-1") ? Optional.of("Taro") : Optional.empty();
    }
}

var name = new Repo().findName("U-2").orElse("(unknown)");
Java

例外の使い分け

  • 呼び手が回復可能なエラー: 戻り値で表すか、チェック例外(throws)で知らせる。
  • 回復不能/契約違反: 非チェック例外(IllegalArgumentException など)で早期に失敗。
public final class FileLoader {
    public String readUtf8(java.nio.file.Path p) throws java.io.IOException {
        return java.nio.file.Files.readString(p, java.nio.charset.StandardCharsets.UTF_8);
    }
}
Java

メソッドチェーンと Fluent API

可変なら this を返す、不変なら新インスタンスを返す

public final class MailBuilder {
    private String to, subject, body;
    public MailBuilder to(String v) { this.to = v; return this; }
    public MailBuilder subject(String v) { this.subject = v; return this; }
    public MailBuilder body(String v) { this.body = v; return this; }
    public Mail build() { return new Mail(to, subject, body); }
}
var mail = new MailBuilder().to("a@b").subject("Hi").body("Hello").build();
Java
public record Money(int amount, String currency) {
    public Money add(int delta) { return new Money(amount + delta, currency); }
}
var m = new Money(100, "JPY").add(50);
Java
  • 指針: チェーン途中で副作用を混ぜない。変換の連鎖→最後に出力が読みやすい。

例題で身につける

例 1: 商品の価格計算(副作用なしの設計)

public final class Product {
    private final String id;
    private final int basePrice;

    public Product(String id, int basePrice) {
        if (id == null || id.isBlank()) throw new IllegalArgumentException("id");
        if (basePrice < 0) throw new IllegalArgumentException("price>=0");
        this.id = id;
        this.basePrice = basePrice;
    }
    public int priceWithTax(double rate) {
        return (int) Math.round(basePrice * (1 + rate));
    }
    public String id() { return id; }
}
Java

「計算は返すだけ」にすると、テストが簡単で安全です。

例 2: 文字列ユーティリティ(static 純粋関数)

public final class Strings {
    private Strings() {}
    public static String normalize(String s) {
        return s == null ? "" : s.trim().replaceAll("\\s+", " ");
    }
    public static boolean isBlank(String s) {
        return s == null || s.trim().isEmpty();
    }
}
Java

個体に依存しない共通処理は static に集約。

例 3: 形の面積とラベル(オーバーライド)

public abstract class Shape {
    public abstract double area();
    public String label() { return "shape"; }
}
public final class Circle extends Shape {
    private final double r;
    public Circle(double r) { this.r = r; }
    @Override public double area() { return Math.PI * r * r; }
    @Override public String label() { return "circle"; }
}
Java

「同じ呼び出しで型ごとに違う結果」が多態性の体験です。


よくあるつまずきと回避

「何でも public」にする

公開が広いほど依存が増え、変更が難しくなります。必要最低限だけ公開し、内部は private へ。

副作用だらけのメソッド

状態を変えるなら必ず検証を入れる。計算は返すだけに寄せると、テスト・並行性に強くなります。

オーバーロードの乱用

引数違いが多いと読み手が迷います。必須+varargs、メソッド名の分離、ビルダー導入で明確化します。

null を前提にする

契約を決め、Optional か早期正規化で「null 地獄」を回避します。


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

メソッドは「オブジェクトの振る舞い」。公開最小、入力検証、戻り値と例外の契約を明確にし、個体の状態を使う動作はインスタンス、共通計算は static に分ける。副作用は必要な場所だけ、不変に寄せると安全性が上がる。オーバーライドで分岐を型へ追い出し、チェーンは this 返し(可変)か新インスタンス返し(不変)で統一する——この型を守れば、読みやすく拡張しやすい振る舞い設計が身につきます。

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