メソッド内部で任意の型のインスタンスを生成してオブジェクトを返す場合
public static <T> T hoge(Class<T> clazz) { try { return clazz.newInstance(); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } }
といったようにjava.lang.Classを引数にとり、リフレクションでインスタンス生成するというのが常道だった。
対象となるClassにはデフォルトコンストラクタ(引数なしのコンストラクタ)が存在することが前提となる。
呼び出し側は以下のように 型名.class を渡す。
String string = hoge(String.class);
これがstataicメソッドではなく、thisオブジェクトが継承による型変数のバインドをしているような特殊なケースでは引数にClassを渡さずともインスタンス生成することも出来たが、限定的なケースである。(参考:new T()したいケースへの対処法 - プログラマーの脳みそ)
裏技的な手法としては
public static <T> T hoge(T ... dummy) { try { Class<?> clazz = dummy.getClass(); Class<?> componentType = clazz.getComponentType(); return (T) componentType.newInstance(); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } }
といったように可変長配列を用い、呼び出し時には
String string = hoge();
といったように引数を渡さないことで空の配列を送り、そこからインスタンスを作成するという手法もあった。
しかし、この手法を用いる場合、空のダミー配列は静的に型が解決されておらねばならず、型変数などで間接的に扱うことはできない。
また、IDEによっては引数が補完されてしまって逆に使いにくいということもマイナス要因だった。
ラムダ or メソッド参照
Java8ではラムダ式やメソッド参照が使えるようになったので、インスタンス生成をこれらで外に引っ張り出すことができる。
public static <T> T hoge(Supplier<T> supplier) { return supplier.get(); }
ここでは標準APIのjava.util.function.Supplierを用いた。
この関数型インタフェースはT get()というメソッドが用意されており、引数なしで値を返す実装を汎用的に表すことができる。
これに対して呼び出し側は
String str1 = hoge(String::new); String str2 = hoge(()->"test");
といった記述になる。
これにより、java.lang.Classを渡してリフレクションでnewInstance()するよりも柔軟なインスタンス生成ができるようになる。デフォルトコンストラクタがある前提を敷く必要もない。
そしてメソッドの実装側もリフレクションに伴う例外処理をする必要がなくなる。
ぜひ、導入を検討してもらいたい。