2011-11-23 9 views
6

Sto utilizzando una factory astratta per restituire istanze di sottoclassi concrete. Vorrei creare un'istanza delle sottoclassi in fase di runtime data una stringa del nome della classe concreta. Devo anche passare un parametro ai costruttori. La struttura di classe è la seguente:Java Instantiate Class in fase di runtime con parametri

abstract class Parent { 

    private static HashMap<String, Child> instances = new HashMap<String,Child>() 

    private Object constructorParameter; 

    public static Child factory(String childName, Object constructorParam){ 

    if(instances.keyExists(childName)){ 
     return instances.get(childName); 
    } 

    //Some code here to instantiate the Child using constructorParam, 
    //then save Child into the HashMap, and then return the Child. 
    //Currently, I am doing: 
    Child instance = (Child) Class.forName(childClass).getConstructor().newInstance(new Object[] {constructorParam}); 
    instances.put(childName, instance); 
    return instance; 
    } 

    //Constructor is protected so unrelated classes can't instantiate 
    protected Parent(Object param){ 
    constructorParameter = param; 
    } 

}//end Parent 

class Child extends Parent { 
    protected Child(Object constructorParameter){ 
     super(constructorParameter); 
    } 
} 

mio attmept sopra sta gettando la seguente eccezione: java.lang.NoSuchMethodException: Child.<init>(), seguita dalla traccia dello stack.

Qualsiasi aiuto è apprezzato. Grazie!

risposta

13
Constructor<?> c = Class.forName(childClass).getDeclaredConstructor(constructorParam.getClass()); 
c.setAccessible(true); 
c.newInstance(new Object[] {constructorParam}); 

Il metodo getConstructor prende Class argomenti di distinguere tra i costruttori. Ma restituisce solo costruttori pubblici, quindi è necessario getDeclaredConstructor(..). Allora avresti bisogno di setAccessible(true)

+0

Ho provato questo e sto ancora vedendo lo stesso errore. Devo modificare le firme del costruttore? Al momento le firme non prevedono esplicitamente parametri di tipo Object, ma di qualcosa di più specifico. – bibs

+0

il tuo 'constructorParam.getClass()' dovrebbe restituire il tipo esatto di parametro che ti aspetti – Bozho

+0

In base all'esempio, non sono sicuro di ciò che fa constructorParam.getClass(). Puoi spiegare meglio la tua risposta? Grazie! – trusktr

3

L'errore: stai invocando il costruttore sbagliato - e il compilatore non ha modo di aiutarti.

Il problema riscontrato era che si stava accedendo al costruttore dell'argomento zero anziché a quello con argomenti. Ricorda che i costruttori di java sono in definitiva solo metodi, anche se speciali - e con la riflessione, tutte le scommesse sono fuori --- il compilatore non ti aiuterà se fai qualcosa di sciocco. Nel tuo caso, hai riscontrato un problema di ambito E anche un problema di firma del metodo allo stesso tempo.

Come risolvere questo problema e non hanno mai a che fare con esso di nuovo in questa applicazione

È una buona idea di avvolgere invocando un costruttore in un metodo di supporto statico che può essere testati direttamente, e poi mettere un test esplicito per loro nei miei test unitari, perché se il costruttore cambia e ti dimentichi di aggiornare il tuo codice di riflessione, vedrai nuovamente questi errori criptici di nuovo in aumento.

Si può anche semplicemente richiamare il costruttore come segue:

public static Child create(Integer i, String s) throws Exception 
{ 
    Constructor c = Class.forName(childClass).getConstructor(new Object[]{Integer.class, String.class}); 
    c.setAccessible(true); 
    Child instance = (Child) c.newInstance(new Object[]{i , s}) ; 
    return instance; 
} 

e naturalmente aggiungere ai test

@Test 
    public void testInvoke() 
    { 
     try{ 
    MyClass.create(1,"test"); 
    } 
    catch(Exception e) 
    { 
     Assert.fail("Invocation failed : check api for reflection classes in " + MyClass.class); 
    } 
    } 
+0

Dove passi effettivamente i parametri al costruttore qui? – bibs

+0

@bibs scusa se ho lasciato quel dettaglio "minore". Naturalmente la chiamata al costruttore dinamico prende come argomento un array di oggetti, che deve corrispondere alla firma del costruttore che si acquisisce dal metodo Class.getConstructor .... – jayunit100