web-dev-qa-db-fra.com

Constructeur appelant Java Reflection avec des types primitifs

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?

43
Steve

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.

129
Andrzej Doyle

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()
18
Plaudit Design

É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));
}
6
Jake

tu peux écrire

int[].class.getComponentType()

ou

Integer.TYPE

ou

int.class
3
user3896501

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.

2
Nikita Rybak

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!

0
Code.IT