J'ai une méthode dans mon framework de test qui crée une instance d'une classe, en fonction des paramètres passés dans:
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();
}
}
Le problème est que cela ne fonctionne pas si le constructeur a des types primitifs, comme suit:
public Range(String name, int lowerBound, int upperBound) { ... }
.test("a", 1, 3);
Résulte en:
Java.lang.NoSuchMethodException: Range.<init>(Java.lang.String, Java.lang.Integer, Java.lang.Integer)
Les ints primitives sont automatiquement cochées dans les versions d'objet, mais comment puis-je les récupérer pour appeler le constructeur?
Utilisez Integer.TYPE
au lieu de Integer.class
.
Selon les Javadocs , il s’agit de "L’instance de classe représentant le type primitif int
".
Vous pouvez également utiliser int.class
. C'est un raccourci pour Integer.TYPE
. Non seulement les classes, même pour les types primitifs, vous pouvez dire type.class
en Java.
Pour référencer des types primitifs, utilisez par exemple:
Integer.TYPE;
Vous aurez besoin de savoir quels arguments passés dans votre méthode sont des valeurs primitives. Vous pouvez le faire avec:
object.getClass().isPrimitive()
Étant donné que les types de primitifs sont automatiquement sélectionnés, l'appel getConstructor(Java.lang.Class<?>... parameterTypes)
échouera. Vous devrez parcourir manuellement les constructeurs disponibles. Si tous les types correspondent, tout va bien. Si certains types ne correspondent pas, mais que le type requis est une primitive ET que le type disponible est la classe wrapper correspondante, vous pouvez utiliser ce constructeur. Voir ci-dessous:
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));
}
tu peux écrire
int[].class.getComponentType()
ou
Integer.TYPE
ou
int.class
Si la valeur primitive int
est automatiquement sélectionnée dans un objet Integer
, il ne s'agit plus d'une primitive. Vous ne pouvez pas dire, à partir de Integer
instance, si elle était int
à un moment donné.
Je suggérerais de passer deux tableaux dans la méthode test
: une avec des types et une autre avec des valeurs. Cela supprimera également toute ambiguïté si vous avez un constructeur MyClass(Object)
et que vous transmettez une chaîne (getConstructor
serait à la recherche de String
constructeur).
De plus, vous ne pouvez pas indiquer le type de paramètre attendu si la valeur du paramètre est null.
Pour vérifier si un type est une primitive ou son wrapper, utilisez:
ClassUtils.isPrimitiveOrWrapper(memberClazz)
Dans le cas où vous voulez vérifier s'il s'agit d'un type spécifique, jetez un coup d'œil à ceci:
https://stackoverflow.com/a/27400967/2739334
En tout cas, @Andrzej Doyle avait complètement raison!