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の発見性と一貫性を高める——この線引きを守れば、読みやすく拡張しやすいインターフェース設計が手に入ります。
