次のようなテンプレートクラスがあります。
class MyClass<T>
{
T field;
public void myMethod()
{
field = new T(); // gives compiler error
}
}
クラスにTの新しいインスタンスを作成するにはどうすればよいですか?
型の消去後、T
について知られていることは、それがObject
のサブクラスであることだけです。 T
のインスタンスを作成するには、何らかのファクトリーを指定する必要があります。
1つのアプローチでは、 Supplier<T>
:
class MyClass<T> {
private final Supplier<? extends T> ctor;
private T field;
MyClass(Supplier<? extends T> ctor) {
this.ctor = Objects.requireNonNull(ctor);
}
public void myMethod() {
field = ctor.get();
}
}
使用法は次のようになります。
MyClass<StringBuilder> it = new MyClass<>(StringBuilder::new);
または、Class<T>
オブジェクト、およびリフレクションを使用します。
class MyClass<T> {
private final Constructor<? extends T> ctor;
private T field;
MyClass(Class<? extends T> impl) throws NoSuchMethodException {
this.ctor = impl.getConstructor();
}
public void myMethod() throws Exception {
field = ctor.newInstance();
}
}
別の非反射アプローチは、ハイブリッドビルダー/抽象ファクトリーパターンを使用することです。
効果的なJavaでは、Joshua BlochはBuilderパターンを詳細に検討し、一般的なBuilderインターフェースを提唱しています。
public interface Builder<T> {
public T build();
}
具象ビルダーはこのインターフェイスを実装でき、外部クラスは具象ビルダーを使用して必要に応じてビルダーを構成できます。ビルダーは、Builder<T>
としてMyClassに渡すことができます。
このパターンを使用すると、T
にコンストラクターパラメーターがあるか、追加の構成が必要な場合でも、T
の新しいインスタンスを取得できます。もちろん、BuilderをMyClassに渡す何らかの方法が必要です。 MyClassに何も渡せない場合は、BuilderとAbstract Factoryが出ています。
これは、探しているものよりも重いかもしれませんが、機能します。このアプローチをとる場合、メソッドが呼び出されるたびにメソッドに渡すのではなく、構築時にMyClassにファクトリを注入する方が意味があることに注意してください。
interface MyFactory<T>
{
T newObject();
}
class MyClass<T>
{
T field;
public void myMethod(MyFactory<T> factory)
{
field = factory.newObject()
}
}
サブクラスを使用する場合は、消去を回避することもできます http://www.artima.com/weblogs/viewpost.jsp?thread=20886