开发者

Java: newInstance of class that has no default constructor

开发者 https://www.devze.com 2023-01-15 14:45 出处:网络
I\'m trying to build an automatic testing framework (based on jUnit, but that\'s no important) for my students\' homework. They will have to create constructors for some classes and also add some meth

I'm trying to build an automatic testing framework (based on jUnit, but that's no important) for my students' homework. They will have to create constructors for some classes and also add some methods to them. Later, with the testing functions I provide, they will check if they开发者_开发知识库 went alright.

What I want to do is, by reflection, create a new instance of some class I want to test. The problem is that, sometimes, there is no default constructor. I don't care about that, I want to create an instance and initialize the instance variables myself. Is there any way of doing this? I'm sorry if this has been asked before, but just I couldn't find any answer.

Thanks in advance.


Call Class.getConstructor() and then Constructor.newInstance() passing in the appropriate arguments. Sample code:

import java.lang.reflect.*;

public class Test {

    public Test(int x) {
        System.out.println("Constuctor called! x = " + x);
    }

    // Don't just declare "throws Exception" in real code!
    public static void main(String[] args) throws Exception {
        Class<Test> clazz = Test.class;
        Constructor<Test> ctor = clazz.getConstructor(int.class);
        Test instance = ctor.newInstance(5);           
    }
}


Here is a generic solution that does not require javassist or another bytecode "manipulator". Although, it assumes that constructors are not doing anything else than simply assigning arguments to corresponding fields, so it simply picks the first constructor and creates an instance with default values (i.e. 0 for int, null for Object etc.).

private <T> T instantiate(Class<T> cls, Map<String, ? extends Object> args) throws Exception
{
    // Create instance of the given class
    final Constructor<T> constr = (Constructor<T>) cls.getConstructors()[0];
    final List<Object> params = new ArrayList<Object>();
    for (Class<?> pType : constr.getParameterTypes())
    {
        params.add((pType.isPrimitive()) ? ClassUtils.primitiveToWrapper(pType).newInstance() : null);
    }
    final T instance = constr.newInstance(params.toArray());

    // Set separate fields
    for (Map.Entry<String, ? extends Object> arg : args.entrySet()) {
        Field f = cls.getDeclaredField(arg.getKey());
        f.setAccessible(true);
        f.set(instance, arg.getValue());
    }

    return instance;
}

P.S. Works with Java 1.5+. The solution also assumes there's no SecurityManager manager that could prevent invocation of f.setAccessible(true).


If you haven't used mocking frameworks (like ezmock) I highly recommend you give one a try.

I may be wrong and that may not help you at all, but from what I could gather from your post it seems possible that mocking may be exactly what you are looking for (Even though I recognize that it has nothing to do with what you asked for.

Edit: In response to comment.

No, Modern mocking frameworks allow you to create a "Fake" instance of any class from "nothing" and pass it around as though it was an instance of the class. It doesn't need an interface, it can be any class. Also methods can be scripted to return a sequence of values from a simple always return "7" to "When called with an arg=7 return 5 the first call, 6 the second and 7 the third".

It's usually used in conjunction with testing frameworks to give a reference class to pass to the class you are testing.

This may not be exactly what you are looking for, but you mentioned unit testing and manually initializing variables so it seemed like this is something that may eventually come in handy.


I used the following code to create a list of generic objects of any type of class name passed in. It uses all of the set methods within the class to set all the values passed in via the result set. I'm posting this in case anyone was interested in it as well.

protected List<Object> FillObject(ResultSet rs, String className)
    {
        List<Object> dList = new ArrayList<Object>();

        try
        {
            ClassLoader classLoader = GenericModel.class.getClassLoader();

            while (rs.next())
            {
                Class reflectionClass = classLoader.loadClass("models." + className);

                Object objectClass = reflectionClass.newInstance();

                Method[] methods = reflectionClass.getMethods();

                for(Method method: methods)
                {
                    if (method.getName().indexOf("set") > -1)
                    {
                        Class[] parameterTypes = method.getParameterTypes();

                        for(Class pT: parameterTypes)
                        {
                            Method setMethod = reflectionClass.getMethod(method.getName(), pT);

                            switch(pT.getName())
                            {
                                case "int":
                                    int intValue = rs.getInt(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, intValue);
                                    break;

                                case "java.util.Date":
                                    Date dateValue = rs.getDate(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, dateValue);
                                    break;

                                case "boolean":
                                    boolean boolValue = rs.getBoolean(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, boolValue);
                                    break;

                                default:
                                    String stringValue = rs.getString(method.getName().replace("set", ""));
                                    setMethod.invoke(objectClass, stringValue);
                                    break;
                            }
                        }
                    }
                }

                dList.add(objectClass);
            }
        }
        catch (Exception e)
        {
            this.setConnectionMessage("ERROR: reflection class loading: " + e.getMessage());
        }

        return dList;
    }


You can use Class.getConstructor or Class.getConstructors and then use the method Constructor.newInstance to initialize the object that you want to use.


You could distribute the following source code with your assignment. Tell the students to include it in their source code. Their code won't compile unless they code an Assignment class with the proper signature. The compiler does the signaure checking for you.

Then your testing program does not need to use reflection. Just instantiate an AssignmentFactory and call the make method with the proper arguments.

If you use this idea, your new challenge will be some students modifying AssignmentFactory to fit their Assignment class (breaking your testing program).

package assignment ;

public class AssignmentFactory
{
     public AssignmentFactory ( )
     {
           super ( ) ;
     }

     public AssignmentFactory make ( .... parameters )
     {
           return new Assignment ( .... arguments ) ;
     }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号