mercredi 25 juillet 2018

Calling Class.newInstance with null arguments causing IllegalArgumentException

What I'm Doing

I'm using extreme amounts of reflection in my code and part of that is dynamically creating class instances reflectively. I am able to obtian the needed types and arguments from the data I'm parsing. I put the data into an Object[] to pass as an argument to Class.newinstance. Some of the arguments are null as I'm making a data cross referencer. This means I'm taking two data sets and cross referencing them using a common Object (the one I'm trying to make specifically right now is Salesrep but this code is dynamic so that can change). This means that when parsing one data set, some of the arguments to create this object should be null because they come from the other data set.

The Problem and What I've tried

Unfortunately whenever I set null arguments I get an Illegal Argument Exception stating that I have the wrong number of arguments. I've checked the documentation for the error and it says its only thrown when the wrapping fails or when there is actually a wrong number of arguments. I tried searching all over google, stack overflow even some github repos and all I found was some questions about method.invoke that suggested doing something like:

//add in non null args to objectArray
Object arg = null;
objectArray.add(arg)
//causes the exception here anyways.
Class.newinstance(objectArray)

I've tried just adding null directly and not wrapping it but that didn't work either. Note that these constructors are not empty or zero argument, there is only one of them and I can confirm that the number of arguments including nulls is equal to the required number. I can also confirm I am passing the right types when the arguments are not null. I'm 99% sure that any type of argument can be null or at the very least that the argument types I need are indeed nullable (mostly primitives and Arrays or other collections). I've read places that when passing null it needs to be wrapped properly and that It can mistake nulls for an empty object array argument. Honestly I just Can't figure out what would work. I've created a try catch block that prints the number of arguments I passed in, their types and It just seems to match everytime but Still it goes to the catch block. Here is My code causing the error:

Code and how to understand it

Couple Notes to understand this: firstLineArray is a String[] of contructor argument names in my data. Not all the constructor arguments are in one of the data sets so this code takes the arguments that are and fills them in with the available data. The others are set to null. This is done using two maps: argsWeHave maps the argument name to the data and argsWeNeed maps the argument name to its type. The two maps see what argument names they have in common and match the data to its type and pass it into the argArrayOut in the order the constructor needs it.

for (int j = 0; j < numberOfArgsNeeded; j++) {
        //uses parameter name to match the argument string to its type in the constructor
        //if the parameter name in the constructor isn't found among the param names we have data for OR
        //if the parameter name in the header isn't found among the param names of the constructor, it adds a null element
        //basically if there is an argument the constructor needs, and we have that argument by name.
        if (argWeHave.containsKey(ParamNames[j]) && argWeNeed.containsKey(firstLineArray[i])){
            //DONE fix where argWeNeed.get(firstLineArray[i]) returns null. Handle it gracefully rather than having NPE.
            argArrayOut.add(stringToType(argArrayIn[i],argWeNeed.get(firstLineArray[i])));
            i++;
        }
        //if there is no match set this argument to null
        else{
            //TODO wrap the null value or make it properly accept null as an arg to the object[] for newinstance.
            Object arg = null;
            argArrayOut.add(arg);
        }
    }
    try {
        //form the new instance with the properly formed argArrayOut.
        object_to_add = (ObjectImplementation) constructor.newInstance(argArrayOut);
    } 
    //exception is thrown after the try. It always hits this catch block

Here is my Stack Trace. First comes what I've printed to the system in the catch block:

Class type: Sales_Rep_Data.SalesRep
number of parameters in constructor: 5
number of arguments passed to constructor: 5
Types needed in constructor
class java.lang.String , class java.lang.String , class java.lang.String , 
class java.lang.String , class java.util.ArrayList
Types provided to constructor
class java.lang.String , class java.lang.String , class java.lang.String , 
null , null 

Then comes the actual stacktrace:

java.lang.IllegalArgumentException: wrong number of arguments
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:488)
at Sales_Rep_Data.ObjectFactory.getObjectImpl(ObjectFactory.java:79)
at Sales_Rep_Data.SalesRepbyId.Parse(SalesRepbyId.java:27)
at Sales_Rep_Data.Data_Parser.main(Data_Parser.java:45)

As you can see based on what I print, I have the required number and types. I just cannot fathom why this error occurs. It must be some remote wrapping requirement or something I'm being silly about. Any Code readability suggestions are welcome. This code is not runnable on its own, it requires alot of other code, this is just a snippet from my ObjectFactory Class.





Aucun commentaire:

Enregistrer un commentaire