57

I have a method in my test framework that creates an instance of a class, depending on the parameters passed in:

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

The problem is, this doesn't work if the constructor has primitive types, as follows:

public Range(String name, int lowerBound, int upperBound) { ... }

.test("a", 1, 3);

Results in:

java.lang.NoSuchMethodException: Range.<init>(java.lang.String, java.lang.Integer, java.lang.Integer)

The primitive ints are auto-boxed in to object versions, but how do I get them back for calling the constructor?

Steve
  • 2,207
  • 6
  • 24
  • 35

6 Answers6

159

Use Integer.TYPE instead of Integer.class.

As per the Javadocs, this is "The Class instance representing the primitive type int."

You can also use int.class. It's a shortcut for Integer.TYPE. Not only classes, even for primitive types you can say type.class in Java.

Matheus Avellar
  • 1,507
  • 1
  • 22
  • 29
Andrzej Doyle
  • 102,507
  • 33
  • 189
  • 228
  • 11
    int.class is a shortcut for Integer.TYPE, for any, even primitive type in Java you can write: type.class – iirekm Oct 13 '10 at 17:40
  • 1
    This is supposed to be an accepted answer. You beat Plaudit Design by few seconds ;-) Just edited to include iirekm's comment. – Karthik Bose Dec 17 '13 at 10:52
21

To reference primitive types use, for example:

Integer.TYPE;

You will need to know which arguments passed into your method are primitive values. You can do this with:

object.getClass().isPrimitive()
Plaudit Design
  • 1,156
  • 8
  • 16
7

Since the primitive types are autoboxed, the getConstructor(java.lang.Class<?>... parameterTypes) call will fail. You will need to manually loop through the available constructors. If all types match then you're fine. If some types do not match, but the required type is a primitive AND the available type is the corresponding wrapper class, then you can use that constructor. See bellow:

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));
}
Jake
  • 71
  • 1
  • 1
6

you can write

int[].class.getComponentType()

or

Integer.TYPE

or

int.class
user3896501
  • 2,987
  • 1
  • 22
  • 25
2

If primitive int value is autoboxed into Integer object, it's not primitive anymore. You can't tell from Integer instance whether it was int at some point.

I would suggest passing two arrays into test method: one with types and another with values. It'll also remove ambiguity if you have a constructor MyClass(Object) and pass string value (getConstructor would be looking for String constructor).
Also, you can't tell expected parameter type if parameter value is null.

Nikita Rybak
  • 67,365
  • 22
  • 157
  • 181
  • `if primitive int value is autoboxed into Integer object, it's not primitive anymore. You can't tell from Integer instance whether it was int at some point` didn't get your this point with the question asked. if a primitive is boxed to Integer than why it doesn't resolve to var args – jmj Oct 13 '10 at 16:09
  • @org.life.java What do you mean, _"doesn't resolve to var args"_ ? The error is because constructor can't be found. Vararg part works smoothly, _int_ is converted into _Integer_ for second and third elements of _constructorArgs_ array (for the simple reason that _int_ can't ba a part of _Object[]_). – Nikita Rybak Oct 13 '10 at 16:14
  • How is this marked as the answer? See below "Use `Integer.TYPE` instead of `Integer.class`." from @Andrzej Doyle and others – a113nw Jun 15 '17 at 23:19
1

To actually check if a type is a primitive or it's wrapper use:

ClassUtils.isPrimitiveOrWrapper(memberClazz)

In the case you want to check if it's a specific type take a look at this:

https://stackoverflow.com/a/27400967/2739334

In any case @Andrzej Doyle was completely right!

Pwnstar
  • 2,333
  • 2
  • 29
  • 52