拡張性/柔軟性を獲得する章

3.7. インターフェースで定数定義

『目的』

インターフェースは定数定義のためだけに使うものではありません。

『Before』

定数定義だけのインターフェースです。

/**
 * 定数定義用のインターフェース
 */
public interface Hoge {
    int NUM_HOGE = 1234;
}

このインターフェースを実装することでクラスでは定数として利用しますが、 その際に、いくつか弊害があります。

まず、「無意味に外部公開してしまう」ということです。 フィールドは「public static final」です。 他のクラスからはこのクラスに属する定数のように見えてしまいます。

また、外部公開しているということは、互換性を保証し続けねばなりません。 つまり、「不要になってもインターフェースを取り除くことができない」ということです。

あと、本来はスーパークラスだけに閉じこめておきたかった定数が、 サブクラスまで出しゃばってきてしまいます。 ということはサブクラスで同名の変数名を定義できません。 つまり、このクラスを継承したサブクラスのすべてにおいて「名前空間が汚染される」といえます。

 ※名前空間の汚染=名前が適切な場所で使われないこと。「名前の衝突」ともいいます。

『After』

定数インターフェースの代わりにいずれかの方法を採るべきです。

「対策1.既存クラスまたはインターフェースに定数を追加」

既存のクラスやインターフェースに強く結びつく場合は、この方法が適切でしょう。
これは、Integerクラスです。

public final class Integer extends Number implements Comparable {
    /**
     * int に設定可能な最小値を保持する定数
     */
    public static final int MIN_VALUE = 0x80000000;
      :
}

「対策2.型の安全性が保証されるクラスで提供」

列挙型のメンバーとして見なす定数ならばこの方法がよいでしょう。
「列挙型の定義」を参照してください。

「対策3.ユーティリティクラスで提供」

いずれでもなければ、この方法でしょう。

/**
 * 定数ユーティリティクラス
 */
public class Hoge {
    public static final int NUM_HOGE = 1234;

    private Hoge() {}  // インスタンス化させない
}

あわせて「ユーティリティクラス」を参照してください。

また、クラス内で定数を頻繁に使う場合、ユーティリティクラスのクラス名が長いと定数名の記述が煩わしくなることがあります。 このような場合はstatic importを使うことにより、定数名のみの記述にすることも検討してください。

// インターフェース単位ではなく、定数単位でインポートする。
import static name.space.Hoge.NUM_HOGE;

    ...
    // NUM_HOGEとして参照可能
    int[] arr = new int[NUM_HOGE];

『まとめ』

インターフェースは、本質としては定数を定義するため用意されたものではありません。
メソッドの無いインターフェースを定義してはいけません。

たしかに、インターフェースで定数を定義しているケースはJDKのソースを眺めるといくつかあります。 たとえば、java.io.ObjectStreamConstantsというインターフェースなどです。
ですが、これらは例外的と見なすべきです。

< 前のページへ

Pagetop