1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Effective Javaを読む】 第2章 項目1 『コンストラクタの代わりにstaticファクトリーメソッドを検討する』

Last updated at Posted at 2020-07-20

はじめに

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ファクトリーメソッドに置き換えてみたら色々嬉しいことあるんじゃないですかねっていってる節

サンプルコード

例1

public static Boolean valueOf(boolean b){
    return b ? Boolean.TRUE : Boolean.FALSE;
}
例2

//サービスプロバイダーフレームワーク
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

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?