Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create new instance of a class by passing Object[] instead of parameter list with reflection

Do you have any clue about how to write such method?

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");
like image 506
PRowLeR Avatar asked Nov 18 '15 08:11

PRowLeR


People also ask

How do you create an instance of a class object?

You can use Class. forName() to get a Class object of the desired class. Then use getConstructor() to find the desired Constructor object. Finally, call newInstance() on that object to get your new instance.

How do you create an instance of a class using reflection?

We can use newInstance() method on the constructor object to instantiate a new instance of the class. Since we use reflection when we don't have the classes information at compile time, we can assign it to Object and then further use reflection to access it's fields and invoke it's methods.

How do you pass a class object as a parameter?

To pass an object as an argument we write the object name as the argument while calling the function the same way we do it for other variables. Syntax: function_name(object_name); Example: In this Example there is a class which has an integer variable 'a' and a function 'add' which takes an object as argument.

How do you create an instance of an object in Java?

Using the new keyword in java is the most basic way to create an object. This is the most common way to create an object in java. Almost 99% of objects are created in this way. By using this method we can call any constructor we want to call (no argument or parameterized constructors).


2 Answers

Probably more flexible approach is to check all the constructors and find the compatible one like this:

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

There are still open issues though like varargs-constructors. Also ambiguity cases will not be resolved like it's done by javac (for example, if you have MyObj(Object) and MyObj(String) constructor, you will not be able to use the latter one are both match).

like image 75
Tagir Valeev Avatar answered Sep 29 '22 07:09

Tagir Valeev


The Class method contains a getConstructor method that takes an array of Class as a parameter, corresponding to the constructor arguments. You have to build this array from your parameter array.

Something like that:

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: as Codebender said, this doesn't work when a subtype is passed as the argument.

like image 44
WilQu Avatar answered Sep 29 '22 06:09

WilQu