Effective Java 第 2 版:第 2 章 オブジェクトの生成と消滅 項目 1

現在、Joshua Bloch の Effective Java 第 2 版を読んでいるので、しばらくはこの本の読書メモを書いていく。

項目 1: コンストラクタの代わりに static ファクトリーメソッドを検討する

インスタンスを生成するためには、コンストラクタを使うのが普通の方法である。 この項目では、コンストラクタの代わりに static ファクトリーメソッドを使ってオブジェクトを生成することの利点と欠点について述べてられている。

static ファクトリーメソッドとは、クラスのインスタンスを返す static メソッドのことである。 例えば、Integer.valueOf は基本データ型 int の変数を引数に取り、基本データクラスを返す static ファクトリーメソッドである。

public class Foo {
    public static void main(String[] args) {
        Integer i = Integer.valueOf(1); // Integer オブジェクトを生成
    }
}

static ファクトリーメソッドの利点

コンストラクタと比較して、static ファクトリーメソッドを使うことの利点は主に以下の 4 項目による。

  1. メソッドなので名前を付けられる
  2. static ファクトリーメソッドが呼ばれるたびに新しいインスタンスを生成する必要がな
  3. static ファクトリーメソッドの戻り値型のサブタイプを返せる
  4. パラメータ化された型(ジェネリクス)のインスタンス生成を省力化できる

1 は、メソッドが名前を持つことで、どのような用途か理解しやすくなるということである。また、同じシグニチャでありながら別の処理を行うコンストラクタは作れないが、static ファクトリーメソッドなら可能である。

2 は、インスタンスの生成を制御して、static フィールドに保持したり、Singleton のように数を調節できるということである(インスタンス制御)。

3 は、java.util.Collections の static ファクトリーメソッド群が例として挙げられる。 これらの static ファクトリーメソッドの戻り値型は List<E>Map<K, V> などのインタフェースになっており、List<Integer> list = Collections.emptyList(); のように、それらの型の変数で生成したオブジェクトを受け取る。 しかし、実際は、それらのインタフェースは何らかの型で実装されている。 つまり、インタフェースと実装の分離により、実装の詳細が隠蔽されている。 これにより、クラスの提供者は利用者に知られずに生成するオブジェクトの変更などを行える。 この点で、static ファクトリーメソッドを使わない場合と比較して、拡張性に富む。

4 は、型パラメータの指定の煩雑さを軽減できるということである。つまり、

Map<Integer, List<Integer>> map = new HashMap<Integer, List<Integer>>();

より、例えば HashMap が newInstance() という static ファクトリーメソッドを持っていたとして、

Map<Integer, List<Integer>> map = HashMap.newInstance();

とする方が楽だということである。 実は、Java 7 以降では、以上の方法を使わずとも、ダイヤモンド構文でコンパイラが型推定してくれる。

Map<Integer, List<Integer>> map = new HashMap<>();

static ファクトリーメソッドの欠点

上述した利点の一方で、static ファクトリーメソッドには以下の欠点がある。

  • public / protected のコンストラクタを持たないクラスのサブクラスを作れない
  • 他の static メソッドと混同してしまう

1 は、public / protected コンストラクタを持たないクラスのサブクラスから、そのようなクラスのコンストラクタにアクセスできないことによる。

2 は、static ファクトリーメソッドへの命名方法を標準的な方法に統一することで、ある程度解決できる問題である。 一般的には、それぞれのメソッドの性質に応じて、以下の名前を付けることが多い。

  • valueOf, of
    • 引数と同じ意味を表すオブジェクトを返すメソッド
  • getInstance
    • 引数から別の意味を持つオブジェクトを返すメソッド。インスタンスを毎回新しく生成するとは限らない
  • newInstance
    • getInstance と違って、必ず新しいインスタンスを生成する
  • getType, newType
    • getInstance, newInstance と同じ働きだが、型 Type のオブジェクトを返す

参考文献

EFFECTIVE JAVA 第2版 (The Java Series)

EFFECTIVE JAVA 第2版 (The Java Series)