自分用メモなので雑です。
コンストラクタはこういうの
public Human
の部分
(フィールド持たない場合は、何も書かなくてもデフォルトのコンストラクタが生成される)
class Human {
String name;
public Human(String name) {
this.name = name;
}
}
コンストラクタを呼ぶ = クラスのインスタンスを生成しても取得する
Human human = new Human("yamada");
staticファクトリメソッドを用意してコンストラクタからはHumanを生成できないようにするとこうなる
class Human {
String name;
// クライアントが、createHumanからしかHumanを作れないようコンストラクタはprivateにしている。
private Human(String name) {
this.name = name;
}
// staticなファクトリメソッド
static Human createHuman(String name) {
return new Human(name);
}
}
(コンストラクタではなく、)staticファクトリメソッドを使わせるメリット
- 1 名前がわかりやすい
- 2 呼び出しごとに新たなオブジェクトを作らなくて済むようになる
呼び出しによって作られるオブジェクトの数なども簡単にコントロールできるようになる。シングルトンパターンがその一例。
public class RegisterNote {
private static RegisterNote registerNote = new RegisterNote();
private RegisterNote(){}
public static RegisterNote getInstance() {
return registerNote;
}
}
上の例では、RegisterNoteがたった一つしか存在しえないことを保証している。
- 3 返す型を柔軟に選べる
例えば、java.util.Collections
はCollection型のオブジェクトを返すファクトリメソッドがたくさん入っている。
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
このemptyList()が返しているEMPTY_LISTはEmptyList<E>
というクラスで、
EmptyListは抽象クラスAbstractListを継承し、AbstractListはインターフェースListを実装し、ListはインターフェースCollectionを拡張したものである。
慣習として、Hogeという名前のインターフェースのstaticファクトリメソッドはHogesというシングルトンのクラスに入れる。
(個人的に、これでライブラリの命名の規則についてかなり合点がいった。sついてるのとついてないのなんだろうって思ってたので)
でもあまり他の例が思いつかない、、、
- 4 引数によって、返すオブジェクトの型を自由に決められる。
例EnumSet
クラス
enum型が64個以下で構成されている場合は、RegularEnumSetInstanceを返し、64個を超える場合は、JumboEnumSetを返す。
これはパフォーマンス上を考えてこうなっているだけで、特にクライアントには知ってほしくない。混乱を招くだけなので。
なので、staticファクトリメソッドの戻り値の型は単にEnumSetで、使う側は自分が作ったのがRegularEnumSetInstanceなのかそれともJumboEnumSetなのかを全く考えず、EnumSetに用意されたAPIを利用すればよい。
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum<?>[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}
- 5 返されるオブジェクトのクラスは、staticファクトリメソッドを書いている時点で存在する必要さえない
ちょっとよくわからなかった。Service Provider Interfaceについてちょっとドキュメント読んだが、なかなか難しい。
ただ、「リフレクションを使わずに」、とあるので、まだ宣言していないクラスも返すようにすることができる的なことだろう()
そういう意味では4を拡大したような話だと思う。たぶん。
java.util.spi.CurrencyNameProviderについてちょっと見たが、おそらく通貨単位の略称("JPY", "USD"など)をもとに、円マークやドル記号をStringとして返してくれるようなサービス(を作るのに?)使われている?あまりわかっていないが、、
staticファクトリメソッドを使うことによって生まれる制約
- 1 コンストラクタがprivateだと、サブクラスが作れない
例: 下記は当然コンパイルエラーとなる。
interface Writable {
void write(String txt);
}
class Paper implements Writable{
private Paper() {
new Paper();
}
public static Paper createPaper() {
return new Paper();
}
@Override
public void write(String txt) {
System.out.println("write to Paper.");
}
}
class RichPaper extends Paper implements Writable {
public RichPaper() {
super(); // 親クラスPaperのコンストラクタはprivateなので、コンパイルエラーとなる
}
}
しかし、これにより、継承よりもコンポジション(親クラスへの委譲によってメソッドを実装すること)を促すことのになるので、これはこれでよいとのこと。
(全然いい感じの例が思いつかなかった)
class RichPaper implements Writable{
private Paper paper;
private RichPaper() {}
RichPaper createRichPaper() {
return new RichPaper();
}
@Override
public void write(String txt) {
paper.write(txt);
}
}
- 2 staticファクトリメソッドだと、探すのが大変
確かに、これは自分も経験がある。とりあえずこのクラスを作りたい!っていうときに、コンストラクタがprivateで、「じゃあどこから作ればいいんだよ」とイラつく。
しかし、staticファクトリメソッドの一般的な命名規則を把握すればあんまり苦労しないかもなと思った。
下記のメソッドのニュアンスの違いは、本に載ってるので省略。
from
of
valueOf
instance
getInstance
create
newInstance
// ここからは、クラス名がHogeの場合
getHoge
newHoge
hoge
## まとめ
それぞれのメリット、デメリットを意識して使い分ける。「staticファクトリメソッドにしたらどうかな?」と検討してみる。