2015-11-18 19 views
5

Hai qualche idea su come scrivere tale metodo?Come creare una nuova istanza di una classe passando Oggetto [] invece di elenco parametri con riflessione

public abstract class AbstractClass{} 

public class TrialClass extends AbstractClass{ 
    public TrialClass(final String a, final String b){} 
    public TrialClass(final String a, final String b, final String c){} 
} 

public class getNewInstance(final Class<? extends AbstractClass> clazz, Object... constructorParameters){ 
    //??? 
} 

TrialClass trialClass = getNewInstance(TrialClass.class, "A", "B"); 
+0

È necessario cercare la matrice di costruttori per il costruttore i cui parametri corrispondono agli argomenti. –

+0

Dove entra in 'AbstractClass'? Perché è necessario? –

+0

E la firma del metodo dovrebbe essere qualcosa come 'public T getNewInstance (classe finale clazz, Object ... cs);' – Codebender

risposta

2

Procedimento Class contiene un metodo getConstructor che accetta un array di Class come parametro, corrispondente ai parametri del costruttore. Devi costruire questo array dal tuo array di parametri.

Qualcosa del genere:

public <T> T getNewInstance(final Class<T> clazz, Object... constructorParameters) throws InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException{ 
    Class[] parameterTypes = new Class[constructorParameters.length]; 
    for(int i = 0; i < constructorParameters.length; i++) { 
     parameterTypes[i] = constructorParameters[i].getClass(); 
    } 

    Constructor<T> constructor = clazz.getConstructor(parameterTypes); 
    return constructor.newInstance(constructorParameters); 
} 

Edit: come ha detto Codebender, questo non funziona quando un sottotipo viene passato come argomento.

+1

Questo non funziona quando viene passato un sottotipo come argomento ... – Codebender

+0

la parte che trova il costruttore è corretta, ma gli argomenti che passano per creare newInstance hanno alcuni problemi. Se chiamiamo questo metodo come: getNewInstance (TrialClass.class, "A", "B"); constructorParameters sarà una matrice Object contenente "A" e "B". Quindi stiamo provando a passare l'array Object al posto di String, i parametri String. Questo codice genererà probabilmente IllegalArgumentException. – PRowLeR

+0

constructor.newInstance (constructorParameters [0], constructorParameters [1]) funzionerebbe per quell'esempio. – PRowLeR

3

approccio Probabilmente più flessibile è quello di controllare tutti i costruttori e trovare quello compatibile in questo modo:

public static <T> T getNewInstance(final Class<T> clazz, Object... constructorParameters) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 
    Constructor<?> candidate = null; 
    for(Constructor<?> constructor : clazz.getConstructors()) { 
     if(Modifier.isPublic(constructor.getModifiers()) && isConstructorCompatible(constructor, constructorParameters)) { 
      if(candidate == null) 
       candidate = constructor; 
      else 
       throw new IllegalArgumentException("Several constructors found which are compatible with given arguments"); 
     } 
    } 
    if(candidate == null) 
     throw new IllegalArgumentException("No constructor found which is compatible with given arguments"); 
    return (T) candidate.newInstance(constructorParameters); 
} 

private static boolean isConstructorCompatible(Constructor<?> constructor, Object[] constructorParameters) { 
    Class<?>[] parameterTypes = constructor.getParameterTypes(); 
    if(parameterTypes.length != constructorParameters.length) 
     return false; 
    for(int i=0; i<parameterTypes.length; i++) 
     if(!isParameterCompatible(parameterTypes[i], constructorParameters[i])) 
      return false; 
    return true; 
} 

private static boolean isParameterCompatible(Class<?> type, Object parameter) { 
    if(parameter == null) 
     return !type.isPrimitive(); 
    if(type.isInstance(parameter)) 
     return true; 
    if(type.isPrimitive()) { 
     if (type == int.class && parameter instanceof Integer 
       || type == char.class && parameter instanceof Character 
       || type == byte.class && parameter instanceof Byte 
       || type == short.class && parameter instanceof Short 
       || type == long.class && parameter instanceof Long 
       || type == float.class && parameter instanceof Float 
       || type == double.class && parameter instanceof Double 
       || type == boolean.class && parameter instanceof Boolean) 
      return true; 
    } 
    return false; 
} 

Ci sono ancora questioni aperte anche se, come varargs-costruttori. Anche i casi di ambiguità non verranno risolti come fa javac (ad esempio, se hai il costruttore MyObj(Object) e MyObj(String), non sarai in grado di utilizzare quest'ultimo come entrambi).