はじめに
Javaの理解を深めるならこれを読めということで、Effective Javaを自分なりに解釈しながら読んでいこうと思います。
読んでるのは積読していた第2版です。
https://www.amazon.co.jp/EFFECTIVE-JAVA-Java-Joshua-Bloch/dp/4621066056/ref=pd_sbs_14_3/355-5139262-7829161?_encoding=UTF8&pd_rd_i=4621066056&pd_rd_r=ac861412-beae-43a8-872a-8b853aa69980&pd_rd_w=oIuWA&pd_rd_wg=nhmjU&pf_rd_p=7642417c-6494-4d06-a2b0-fcb0e0b3c563&pf_rd_r=HAEC02ASTQVJ4SPSM92Q&psc=1&refRID=HAEC02ASTQVJ4SPSM92Q
コンストラクタの代わりにstaticファクトリーメソッドを検討する
今までコンストラクタを使って実装していた処理を、staticファクトリーメソッドに置き換えてみたら色々嬉しいことあるんじゃないですかねっていってる節
サンプルコード
public static Boolean valueOf(boolean b){
return b ? Boolean.TRUE : Boolean.FALSE;
}
//サービスプロバイダーフレームワーク
public interface Service{
//サービス固有のメソッドをここに
}
//サービスプロバイダーインターフェース
public interface Provider{
Service newService();
}
//サービス登録とアクセスのためのインスタンス化不可能クラス
public class Service{
private Service(){} //インスタンス化を抑制(項目4)
//サービス名をサービスと対応づける
private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = "<def>";
//プロバイダー登録API
public static void registerDefaultProvider(Provider p){
registerProvider(DEFAULT_PROVIDER_NAME,p);
}
public static void registerProvider(String name,Provider p){
providers.put(name,p);
}
//サービスアクセスAPI
public static Service newInstance(){
return newInstance(DEFAULT_PROVIDER_NAME);
}
public static Service newInstance(String name){
Provider p = providers.get(name);
if (p == null)
throw new IllegalArgumentException(
"Nn provider registered with name:" + name
);
return p.newService();
}
}
用語集
コンストラクタ
・コンストラクタとは、クラスのインスタンスを作成するときに実行される処理のこと
SampleClass instanceA = new SampleClass();
↑newの後ろのSampleClasss()の記述がコンストラクタの呼び出し
・コンストラクタの書き方:クラス名と同じにする
public class SampleClass {
//コンストラクタ
public SampleClass(){
System.out.println("コンストラクタだよー");
}
}
↑これでnewした時に「コンストラクタだよー」が標準出力される
・メソッドと違ってコンストラクタは値を返さない(returnを書くとエラーになる)
static
・インスタンスに依存しないメソッドや変数を作るための修飾詞
staticファクトリーメソッド
・staticファクトリーメソッドとは、オブジェクトを返す単なるstaticのメソッドのこと
・例1ではboolean基本データ値(b)をBooleanオブジェクトに変換している
デザインパターンのファクトリーメソッド
・名前が似てるけどstaticファクトリーメソッドとは別物
・ここでは説明は割愛
インターフェース
・インターフェースとは、クラス内にあるメソッドの具体的な処理は書かずに「変数」または「メソッドの型」を記述したもの
・「すべてのメソッドが抽象メソッドであること」「基本的にフィールドを1つも持っていないこと」が条件
・処理内容を具体的に書いていないため、使いたいときに処理内容を実装すればい良いので、将来的に処理の変更が起こりうる場合などに便利(細かいところを後回しにできる)
staticファクトリーメソッドの4つの長所と2つの短所
長所1 コンストラクタと異なり名前を持つ
・コンストラクタはnewすると勝手に実行されちゃうけど、staticファクトリーメソッドならいい感じの名前が付けられるので可読性があがる
例えば確率的素数(probable prime)であるBigIntegerを返す処理があるとして、
-コンストラクタでの書き方
BigInteger(int, int, Random)
-staticファクトリーメソッドでの書き方
BigInteger.probablePrime
…どっちがわかり易い?
長所2 コンストラクタと異なりメソッドが呼び出されるごとに新たなオブジェクトを生成する必要がない
・当たり前だけど、staticなので何にもインスタンス化していない
コンストラクタを呼び出すためにはnewしなくちゃいけない
・不必要に新しいオブジェクトをたくさん作っちゃうのはあんまりよろしくないよねって話
長所3 コンストラクタと異なりメソッドの戻り値型の任意のサブタイプのオブジェクトでも返すことができる
・コンストラクタは戻り値を返さないものだけど、staticファクトリーメソッドはあくまでメソッドなのでreturnすることができるので柔軟性がある
・例2のようなサービスプロバイダーフレームワーク(中身を隠蔽してAPIとして使ってもらう)もつくれちゃうぜ
長所4 パラメータ化された型のインスタンス生成の面倒さを低減する
・冗長な書き方を簡略化できる
例えばコンストラクタだとこう書かなきゃいけなかったものが
Map<String, List<String>> m =
new HashMap<String, List<String>>();
staticファクトリーメソッドだとこう書ける
Map<String, List<String>> m = HashMap.newInstance();
簡潔ですよね
もちろん裏ではstaticファクトリーメソッドをこんな感じで実装している
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K. V>;
}
※P.S. Java7からは型類推をしてくれるのでstaticファクトリーメソッドは書かなくてもいい
短所1 publicあるいはprotectedのコンストラクタをもたないクラスのサブクラスを作れない
短所2 それらが容易に他のstaticメソッドと区別がつかない
・コンストラクタと違ってJavadocがうまく認識してくれない
staticファクトリーメソッドの一般的な名前
名前 | 役割 |
---|---|
valueOf | パラメータと同じ値を持つインスタンスを返す。実質的に型変換メソッドである。 |
of | valueOfの代替。より簡潔にしたもの。 |
getInstance | パラメータで指定されたインスタンスを返すが、同じ値を持つとは言えない。シングルトンの場合には、getInstanceは何も引数を取らず、その唯一のインスタンスが返される。 |
newInstance | getInstanceに似ているが、newInstanceは返される個々のインスタンスは、すべて別々のインスタンスである点が異なる。 |
getType | getInstanceに似ているが、ファクトリーメソッドが対象のクラスと異なるクラスにある場合に使用され。Typeはファクトリーメソッドから返されるオブジェクトの型を示す。 |
newType | newInstanceに似ているが、ファクトリーメソッドが対象のクラスと異なるクラスにある場合に使用される。Typeはファクトリーメソッドから返されるオブジェクトの型を示す。 |
続く
【Effective Javaを読む】 第2章 項目2 『数多くのコンストラクタパラメータに直面した時にはビルダーを検討する』
https://qiita.com/Natsukii/items/eb8fec0d8cae567f6647