0
0

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 5 years have passed since last update.

どうしても「new T()」したい!(しかも検査例外なしで)

Posted at

結論

T型のインスタンスを生成する関数型インターフェースのインスタンスを、引数で受け取る。

// newT()が必要なメソッド
<T> T doSomething(Supplier<T> supplier) {
    T instance = supplier.get();
    return instance;
}

// doSomethingを呼び出すメソッド
void caller() {
    Test instance = this.doSomething(() -> new Test());
}

解説

そもそも new T()がアンチパターンなのはわかっていても、 new T() したいときもある。
そのときの解決法としてよく提示されるのが以下のパターン。

ClassインスタンスをもらってnewInstance()する


<T> T doSomething(Class<T> clazz) throws InstantiationException, IllegalAccessException {
    T instance = clazz.newIntance();
    return instance;
}

void caller() {
   Test instance = this.doSomething(Test.class);
}

このコードの問題点は検査例外をスローすること。

検査例外をスローする

上述のコードの通り、 InstantiationExceptionIllegalAccessExceptionをスローする。
Class.newInstance() 側としては妥当な設計だとは思うが、使う側としてはコンストラクタが例外をスローしないことを確信できる場合もあるわけで…。
そうするとわざわざ上記例外を try-catch したり throws したりするのは面倒くさい。
さらに検査例外なので、streamと相性が悪く1、内部でtry-catchが必要になる

<T extends SuperTest> List<T> doSomething(Class<T> clazz, List<String> valueList) throws InstantiationException, IllegalAccessException {
    return valueList.stream()
        .map(value -> {
            // せっかくstreamでスマートに書きたいのにtry-catch…
            try {
                T obj = clazz.newInstance();
                obj.setValue(value);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        })
        .collect(Collectors.toList());
}

その点、関数型インターフェースは余計な検査例外が発生しないので比較的綺麗にかける(コンストラクタにthrowsがある場合は別)。

余談

コンストラクタが引数をとる場合はFunction<T, R>のインスタンスを使う。
2つならBiFunction<T, U, R>
3つ以上の場合は、引数のためのクラスを作るなり、java.util.Mapを使うなりする。

引数が1つ
// newT()が必要なメソッド
<T> T doSomething(Function<String, T> func) {
    T instance = func.apply("コンストラクタの引数");
    return instance;
}

// doSomethingを呼び出すメソッド
void caller() {
    Test instance = this.doSomething((str) -> new Test(str));
}
引数が2つ
// newT()が必要なメソッド
<T> T doSomething(BiFunction<String, Integer, T> func) {
    T instance = func.apply("コンストラクタの引数", 1);
    return instance;
}

// doSomethingを呼び出すメソッド
void caller() {
    Test instance = this.doSomething((str, number) -> new Test(str, number));
}
  1. 正確には関数型インターフェースと相性が悪い。

0
0
1

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?