In meinem Testframework gibt es eine Methode, die abhängig von den übergebenen Parametern eine Instanz einer Klasse erstellt:
public void test(Object... constructorArgs) throws Exception {
Constructor<T> con;
if (constructorArgs.length > 0) {
Class<?>[] parameterTypes = new Class<?>[constructorArgs.length];
for (int i = 0; i < constructorArgs.length; i++) {
parameterTypes[i] = constructorArgs[i].getClass();
}
con = clazz.getConstructor(parameterTypes);
} else {
con = clazz.getConstructor();
}
}
Das Problem ist, dass dies nicht funktioniert, wenn der Konstruktor wie folgt primitive Typen hat:
public Range(String name, int lowerBound, int upperBound) { ... }
.test("a", 1, 3);
Ergebnisse in:
Java.lang.NoSuchMethodException: Range.<init>(Java.lang.String, Java.lang.Integer, Java.lang.Integer)
Die primitiven Ints werden automatisch in Objektversionen eingebettet, aber wie bekomme ich sie zum Aufruf des Konstruktors zurück?
Verwenden Sie Integer.TYPE
anstelle von Integer.class
.
Gemäß den Javadocs ist dies "Die Klasseninstanz, die den primitiven Typ int
darstellt."
Sie können auch int.class
verwenden. Es ist eine Abkürzung für Integer.TYPE
. Nicht nur Klassen, auch für einfache Typen können Sie in Java type.class
sagen.
Um auf Primitivtypen zu verweisen, verwenden Sie zum Beispiel:
Integer.TYPE;
Sie müssen wissen, welche Argumente in Ihrer Methode primitive Werte sind. Sie können dies mit:
object.getClass().isPrimitive()
Da die primitiven Typen autoboxiert sind, schlägt der Aufruf von getConstructor(Java.lang.Class<?>... parameterTypes)
fehl. Sie müssen die verfügbaren Konstruktoren manuell durchlaufen. Wenn alle Typen übereinstimmen, geht es Ihnen gut. Wenn einige Typen nicht übereinstimmen, der erforderliche Typ jedoch ein Primitiv ist UND der verfügbare Typ die entsprechende Wrapper-Klasse ist, können Sie diesen Konstruktor verwenden. Siehe unten:
static <C> Constructor<C> getAppropriateConstructor(Class<C> c, Object[] initArgs){
if(initArgs == null)
initArgs = new Object[0];
for(Constructor con : c.getDeclaredConstructors()){
Class[] types = con.getParameterTypes();
if(types.length!=initArgs.length)
continue;
boolean match = true;
for(int i = 0; i < types.length; i++){
Class need = types[i], got = initArgs[i].getClass();
if(!need.isAssignableFrom(got)){
if(need.isPrimitive()){
match = (int.class.equals(need) && Integer.class.equals(got))
|| (long.class.equals(need) && Long.class.equals(got))
|| (char.class.equals(need) && Character.class.equals(got))
|| (short.class.equals(need) && Short.class.equals(got))
|| (boolean.class.equals(need) && Boolean.class.equals(got))
|| (byte.class.equals(need) && Byte.class.equals(got));
}else{
match = false;
}
}
if(!match)
break;
}
if(match)
return con;
}
throw new IllegalArgumentException("Cannot find an appropriate constructor for class " + c + " and arguments " + Arrays.toString(initArgs));
}
du kannst schreiben
int[].class.getComponentType()
oder
Integer.TYPE
oder
int.class
Wenn der primitive int
-Wert in das Integer
-Objekt autoboxed wird, ist er nicht mehr primitiv. Sie können der Integer
-Instanz nicht entnehmen, ob es zu einem bestimmten Zeitpunkt int
war.
Ich würde vorschlagen, zwei Arrays in die test
-Methode zu übergeben: eines mit Typen und eines mit Werten. Es wird auch die Mehrdeutigkeit aufheben, wenn Sie einen Konstruktor MyClass(Object)
haben und den Stringwert übergeben (getConstructor
würde nach String
-Konstruktor suchen).
Sie können den erwarteten Parametertyp auch nicht angeben, wenn der Parameterwert null ist.
Um zu überprüfen, ob ein Typ ein Primitiv ist oder sein Wrapper, verwenden Sie:
ClassUtils.isPrimitiveOrWrapper(memberClazz)
Wenn Sie prüfen möchten, ob es sich um einen bestimmten Typ handelt, sehen Sie sich Folgendes an:
https://stackoverflow.com/a/27400967/2739334
Auf jeden Fall hatte @Andrzej Doyle vollkommen recht!