2016-03-09 17 views
14

スーパークラスのサブクラスであるクラスのレジストリを作成したいと思います。クラスはレジストリとして機能するマップに格納されます。キーはレジストリからキーが選択され、そのクラスのインスタンスはリフレクションによって作成されます。スーパークラスからコンストラクタを使用してサブクラスのインスタンスを作成する方法

スーパークラスのコンストラクタ(1パラメータ付き)に応じてクラスをインスタンス化したいと思います。それは、サブクラスでコンストラクタを宣言した場合にのみ機能します。

スーパークラスのコンストラクタを使用してクラスをインスタンス化する方法はありますか?そのコードを型安全にする方法はありますか?

例コード:

public class ReflectionTest { 

    /** 
    * Base class with no-args constructor and another constructor with 1 parameter 
    */ 
    public static class BaseClass { 

     Object object; 

     public BaseClass() { 
      System.out.println("Constructor with no args"); 
     } 

     public BaseClass(Object object) { 
      this.object = object; 
      System.out.println("Constructor with parameter= " + object); 
     } 

     public String toString() { 
      return "Object = " + object; 
     } 
    } 

    /** 
    * Subclass with 1 parameter constructor 
    */ 
    public static class SubClass1 extends BaseClass { 
     public SubClass1(Object object) { 
      super(object); 
     } 
    } 

    /** 
    * Subclass with no-args constructor 
    */ 
    public static class SubClass2 extends BaseClass { 

    } 

    public static void main(String[] args) { 

     // registry for classes 
     Map<Integer,Class<?>> registry = new HashMap<>(); 
     registry.put(0, SubClass1.class); 
     registry.put(1, SubClass2.class); 

     // iterate through classes and create instances 
     for(Integer key: registry.keySet()) { 

      // get class from registry 
      Class<?> clazz = registry.get(key); 

      try { 

       // get constructor with parameter 
       Constructor constructor = clazz.getDeclaredConstructor(Object.class); 

       // instantiate class 
       BaseClass instance = (BaseClass) constructor.newInstance(key); 

       // logging 
       System.out.println("Instance for key " + key + ", " + instance); 

      } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 
       e.printStackTrace(); 
      } 

     } 

     System.exit(0); 
    } 
} 

例では、次のコンソール出力を与える:

Constructor with parameter= 0 
Instance for key 0, Object = 0 
java.lang.NoSuchMethodException: swing.table.ReflectionTest$SubClass2.<init>(java.lang.Object) 
    at java.lang.Class.getConstructor0(Class.java:3082) 
    at java.lang.Class.getConstructor(Class.java:1825) 
    at swing.table.ReflectionTest.main(ReflectionTest.java:63) 
+0

特定のサブクラスに0コンストラクタと1引数コンストラクタの両方があった場合はどうしますか? –

答えて

9
  1. スーパークラスはその子を認識していませんが、これは明らかな理由からそうでなければなりません。
  2. コンストラクタは継承されません。

したがって、サブクラスctorについての前提をせずに、目的のコードを記述することはできません。

あなたは何をすることができますか? 抽象工場パターンを使用してください。

我々はinterface Factoryを作成:

@FunctionalInterface 
public interface SuperclassFactory { 
    Superclass newInstance(Object o); 
} 

あなたはFactory上に複数のメソッドを作成することもできますが、それはラムダのために、それはあまりきちんとなるだろう。

今、あなたがMap<Integer, SuperclassFactory>を持っており、それを移入:、

for(final Map.Entry<Integer,SuperclassFactory> e: registry.entrySet()) { 
    //... 
    final BaseClass instance = e.getValue().newInstance(e.getKey()); 
    //... 
} 

あなたのサブクラスが適切なのctorを持っていない場合:

Map<Integer,SuperclassFactory> registry = new HashMap<>(); 
registry.put(0, SubClass1::new); 
registry.put(1, SubClass2::new); 

をので、あなたは、単に行うこのMapを使用するために、このコードは、使用できるctor参照が存在しないため、コンパイルされません。これはGood Thing(TM)です。あなたが使用する必要があり、現在のSubclass2でコンパイルするためには:

registry.put(1, obj -> new SubClass2()); 

だからあなたが今持っている:

  1. は反射
  2. 取得し、コンパイル時の型の安全性
  3. は醜いキャストを失った失いました(それは反射の誤用によるものであったが)

NB MapentrySet()ではなく、keySet()です。

+1

'e.getValue()。apply(e.getKey())'は 'e.getValue()。newInstace(e.getKey())'でなければなりません。 – saka1029

+0

@ saka1029 yup - ありがとう。 –

+0

きれいで安全な方法で問題を確実に処理する偉大な答え(私はそこにもっと多くがあることを望みます)。私に[この質問]を思い出させる(http://stackoverflow.com/questions/35627246/instantiate-an-extended-class-or-its-parent-depending-on-circumstances)。 – Spotted

1

コンストラクタはサブクラスで明示的に定義される必要があります。スーパークラスでコンストラクタが定義されている場合、リフレクションを使用しているかどうかに関係なく、コンストラクタを使用してサブクラスのインスタンスを作成できます。

SubClass2には1つの引数を持つコンストラクタがないため、1つの引数でインスタンスを作成しようとすると、NoSuchMethodExceptionがスローされます。

1
  • clazz.getDeclaredConstructors()を使用して、クラスのすべてのコンストラクタを取得します。
  • これらを繰り返して、適切なコンストラクタを見つけます。単一のObject-argコンストラクタが利用できない場合は、zero-argsを1つ選択します。
  • 適切なパラメータを使用して、そのコンストラクタを呼び出します。

特定のクラスに適用可能な公共コンストラクタが存在するかどうかを事前に知ることができないため、これは完全に安全な方法ではありません。 ctorがプライベートであるか、使用可能なコンストラクタが必要な型のパラメータを受け付けない可能性があります(たとえば、Stringが必要ですが、Objectのみを持つなど)。

+0

反射の魔法では、「プライベート」は妨げにはならないと主張するかもしれない。しかし、そのような狂気は... –

0

あなたは、コンストラクタ

clazz.getDeclaredConstructor(Object.class); 

を取得するには、このラインを使用しているしかし、あなたのSubclass2は、例外がスローされますので、単一引数のコンストラクタを持っていません。代わり

使用clazz.getDeclaredConstructors()方法とパラメータの数に基づいてコンストラクタを呼び出します。

  BaseClass instance; 
      // get constructor with parameter 
      Constructor constructor = clazz.getDeclaredConstructors()[0]; 
      if (constructor.getParameterCount() == 1) { 
       instance = (BaseClass) constructor.newInstance(key); 
      } else { 
       instance = (BaseClass) constructor.newInstance(); 
      } 
+0

そして 'String'を取るctorと' number'を取るctorを持っていればどうなりますか?これらのパラメータの_types_を確認する必要があります。 –

+0

これはコードの問題ではありませんでしたか? BaseClassインスタンス=(BaseClass)コンストラクタ。newInstance(key); 彼は明示的にパラメータを渡してインスタンスを作成したかったのです。 文字列を渡したい場合、コンストラクタでKEYを渡しませんでした。 – mirmdasif

+0

確かに、この正確な例ではありません。しかし、OPは一般的な解決策を探しています - 私はOPが何らかの形でこの1つの問題を完全に解決することができると確信しています。この質問は、一般的に_これを行う方法を尋ねます - これは完全な答えではありません。 –

関連する問題