2015-11-18 3 views
5

Haben Sie eine Ahnung, wie Sie eine solche Methode schreiben?So erstellen Sie eine neue Instanz einer Klasse, indem Sie Object [] anstelle von Parameterliste mit Reflektion übergeben

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

Sie müssen das Array von Konstruktoren für den Konstruktor suchen, dessen Parameter mit den Argumenten übereinstimmen. –

+0

Woher kommt 'AbstractClass'? Warum wird es benötigt? –

+0

Und Ihre Methodensignatur sollte so etwas wie 'public T getNewInstance sein (letzte Klasse clazz, Object ... cs);' – Codebender

Antwort

2

Class Die Methode enthält eine getConstructor Methode, die ein Array von Class als Parameter, die den Konstruktor Argumente annimmt. Sie müssen dieses Array aus Ihrem Parameter-Array erstellen.

So etwas:

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: als Codebender sagte, dies funktioniert nicht, wenn ein Subtyp als Argument übergeben wird.

+1

Dies funktioniert nicht, wenn ein Subtyp als Argument übergeben wird ... – Codebender

+0

Der Teil, der den Konstruktor findet, ist korrekt, aber die Übergabe von Argumenten zum Erstellen von newInstance hat einige Probleme. Wenn wir diese Methode wie folgt aufrufen: getNewInstance (TrialClass.class, "A", "B"); constructorParameters wird ein Objekt-Array sein, das "A" und "B" enthält. Wir versuchen also, Object-Array anstelle von String, String-Parametern zu übergeben. Dieser Code wird wahrscheinlich IllegalArgumentException auslösen. – PRowLeR

+0

constructor.newInstance (constructorParameters [0], constructorParameters [1]) würde für dieses Beispiel funktionieren. – PRowLeR

3

Wahrscheinlich flexibleren Ansatz ist es, alle Konstrukteure zu überprüfen und die kompatibles wie diese finden:

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; 
} 

Es gibt noch offene Fragen, obwohl wie varargs-Konstrukteuren. Auch Mehrdeutigkeitsfälle werden nicht wie bei javac gelöst (wenn Sie zum Beispiel MyObj(Object) und Konstruktor haben, können Sie den letzteren nicht verwenden).

Verwandte Themen