Java | オブジェクト指向:static メソッド(interface)

Java Java
スポンサーリンク

interface の static メソッドとは

インターフェースの static メソッドは「インターフェース名から直接呼ぶユーティリティ関数」です。インスタンスや実装クラスは不要で、契約(インターフェース)に関連する“純粋な補助処理”や“検証・変換ロジック”をまとめて置けます。Java 8 以降で使え、状態(フィールド)は持たず、副作用の少ない小さな処理に向いています。


何が嬉しいのか(重要ポイントの深掘り)

インターフェースに関係するユーティリティを、別のユーティリティクラスへ散らさず「契約の隣」に置けます。これで見つけやすさ・一貫性が上がり、APIの意図が明確になります。呼び出し側はインターフェース名で静的呼び出しできるため、依存が軽く、テストも容易です。default と違い、実装クラスのメソッドではなく“契約そのものに紐づく関数”として扱えるため、設計が整理されます。


基本の書き方と呼び出し方

宣言と呼び出し

interface Normalizer {
    static boolean empty(String s) {               // static(インターフェース直下の関数)
        return s == null || s.isBlank();
    }
    default String apply(String s) {               // default(既定実装)
        return s == null ? "" : s.trim();
    }
}

final class LowerNormalizer implements Normalizer {
    @Override
    public String apply(String s) {
        if (Normalizer.empty(s)) return "";        // インターフェース名から呼ぶ
        return s.trim().toLowerCase();
    }
}

// 呼び出し側
String a = Normalizer.empty("   ") ? "" : "ok";    // 実装インスタンス不要
Java

ポイントは「インターフェース名.メソッド」で呼ぶこと。実装クラスやインスタンスからは呼びません(そもそも static はインスタンスに属しません)。


default と static の違い(設計の線引き)

default は“既定の振る舞い”、static は“契約に付随する純関数”

  • default は実装クラスで上書き可能な“軽い既定実装”。インスタンスの振る舞い(format、apply など)に使います。
  • static は上書き不可で“契約の補助関数”。検証、変換、パース、小さなヘルパーなど、インスタンス不要の処理に使います。

「インスタンスの状態や差し替えに関係するなら default、関数的で普遍的なら static」と覚えると迷いません。


実用例で身につける

例 1: 検証ヘルパーを契約の隣に置く

interface EmailFormat {
    static boolean looksValid(String raw) {
        if (raw == null) return false;
        String s = raw.trim().toLowerCase(java.util.Locale.ROOT);
        return s.contains("@") && s.indexOf('@') > 0 && s.lastIndexOf('.') > s.indexOf('@');
    }
    String normalize(String raw);
}

final class SimpleEmail implements EmailFormat {
    @Override
    public String normalize(String raw) {
        if (!EmailFormat.looksValid(raw)) throw new IllegalArgumentException("invalid email");
        return raw.trim().toLowerCase(java.util.Locale.ROOT);
    }
}
Java

検証ロジックを EmailFormat に集約することで、どの実装でも同じ基準を共有できます。

例 2: パース関数を置いて、実装は振る舞いだけに集中

interface ProductCode {
    static String parse(String raw) {
        String v = raw == null ? "" : raw.trim().toUpperCase();
        if (v.isBlank()) throw new IllegalArgumentException("code required");
        return v;
    }
    String value();
}

final class DefaultProductCode implements ProductCode {
    private final String v;
    DefaultProductCode(String raw) { this.v = ProductCode.parse(raw); }
    @Override public String value() { return v; }
}
Java

生成前の前処理・検証を契約側へ。実装は「保持と提供」に専念できます。

例 3: 役割ごとの簡潔なユーティリティ

interface Urls {
    static boolean isHttp(java.net.URI u) {
        return u != null && "http".equalsIgnoreCase(u.getScheme());
    }
    static boolean isHttps(java.net.URI u) {
        return u != null && "https".equalsIgnoreCase(u.getScheme());
    }
}
Java

“URL関連の契約”の隣にヘルパーを置くと、利用者が迷いません。


つまずきやすいポイントと回避(重要)

実装クラスやインスタンスから呼ばない

static はインターフェース名から呼ぶのが正道です。実装クラスやインスタンス経由で呼ぶと、意図がぼやけます。

状態を前提にした static を書かない

インターフェースは状態を持てません。外部可変状態に強く依存する static は設計のにおい。純関数(入力→出力)に寄せましょう。

default の乱用で“疑似基底クラス化”しない

重い骨格や順序制御は抽象クラスへ。インターフェースは契約中心、static は軽い補助、default は薄い既定動作に留めてバランスを保ちます。

名前と配置を一貫させる

“契約に付随する関数”だけを static に。汎用ユーティリティは別ユーティリティクラスへ分離し、インターフェースをゴミ箱にしないこと。


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

interface の static メソッドは「契約に付随する純粋なヘルパー」をインターフェース名で直接呼ぶための入口です。default(既定の振る舞い)と役割を分け、状態を前提にしない関数的な設計に徹する。検証・パース・小さな変換を契約の隣へ集約して、APIの発見性と一貫性を高める——この線引きを守れば、読みやすく拡張しやすいインターフェース設計が手に入ります。

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