Java: newInstance of class that has no default constructor

53,664

Solution 1

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

Solution 2

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).

Solution 3

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.

Solution 4

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;
    }
Share:
53,664

Related videos on Youtube

GermanK
Author by

GermanK

Updated on September 20, 2020

Comments

  • GermanK
    GermanK over 3 years

    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.

  • bwawok
    bwawok over 13 years
    It will involve some messy reflection to get a constructor, and walk it, giving an appropriate value for each argument...
  • GermanK
    GermanK over 13 years
    Thanks. The problem is that i don't know if they already added the constructor or not. Of course, i could check if they did by catching the apropriate exception. But i wouldn't know if they created the constructor with the correct arguments. Even worse, i don't know if the constructor work OK. I'd like to build the instance without depending on their implementation.
  • GermanK
    GermanK over 13 years
    With Class.getConstructor or Class.getDeclaredConstructor with no parameters you get a java.lang.NoSuchMethodException if there is no default constructor declared
  • Jon Skeet
    Jon Skeet over 13 years
    @GermanK: Use Class.getConstructors() then instead and see what's available. You have to depend on an implementation in order to instantiate a class. If you create an instance without calling one of their constructors with appropriate arguments, you're not playing fair to their classes, which would expect to be instantiated properly. I suggest you mandate a particular signature.
  • TheLQ
    TheLQ over 13 years
    @GermanK Then have a vararg at the end of your test method where the user inputs the necessary arguments
  • GermanK
    GermanK over 13 years
    @Jon Skeet: I do mandate a particular signature. What if they don't respect it? One factible option i have is to test their Constructor and tell them that until they don't build up that part correctly, they won't be able to test any other method. If there is no better alternative, i think that's what i'll stick with.
  • GermanK
    GermanK over 13 years
    i think this requires some interface that the mocking framework will implement, right? Because i don't have interfaces... They are very simple classes the ones that the students will implement.
  • Jon Skeet
    Jon Skeet over 13 years
    @GermanK: If they don't implement that constructor, the test should fail, surely. If you've specified particular constructor then I suggest you just call getConstructor() with the appropriate types: if an exception is thrown, so be it - the test will fail, which is the correct behaviour. You could explicitly look for the constructor first (using getConstructors()) and fail with a more explicit error message, of course.
  • emory
    emory over 13 years
    @GermanK, u r the prof. If the student did not do the assignment properly, the student has failed. Give them feedback about why they failed. Next time, they will be more careful.
  • GermanK
    GermanK over 13 years
    This just will take part of the testing (signature correctness) to compile time... What would happen if they don't initialize correctly the instance variables? I'd still need to test them anyway. On the other hand, i don't want to add anything that would distract them from their main goal in their assignment.
  • emory
    emory over 13 years
    Yes, you would still need to evaluate their assignment. The purpose of the AssignmentFactory is to try to force them to submit their assignment in a format that is suitable for programmatic evaluation.
  • GermanK
    GermanK over 13 years
    Thanks everyone. I've been wondering around the javassist library which can handle modifications on runtime of Java classes. The problem is that i'd have to mess with the class loaders on JUnit to add the modifications I'd need before the class is efectively loaded, and it'd be too much mess. So i'm finally staying with this much simpler solution: ask them to build the constructors.
  • NT_
    NT_ over 10 years
    this is nice but I think it should be: params.add((pType.isPrimitive()) ? 0 : null);
  • Vlad
    Vlad over 10 years
    @NT_ Good spot. Though simply passing zero won't work, as a correct type is required. newInstance() will work after converting pType to a wrapper class (e.g., ClassUtils from apache-commons can be used).
  • NT_
    NT_ over 10 years
    Uhm, what do you mean? It does seem to work for me. The compiler will do the necessary narrowing/expansion and boxing required and 0 will convert to all primitives' default value. I'm using this for quite a while now without problems...
  • Vlad
    Vlad over 10 years
    Compiler is not able to catch it, as pType's real type is known only in run-time as well as the constructor parameters type matching is done in run-time. Possibly you used it with compatible types (e.g. int), by try with a field of type 'char'.
  • Gunnar Bernstein
    Gunnar Bernstein about 9 years
    Why is Constructor<Test> ctor = clazz.getConstructor(int.class); giving me a no such method exception?
  • Jon Skeet
    Jon Skeet about 9 years
    @GunnarBernstein: I don't know - I've just copied, pasted, compiled and run the code in my answer and it still works for me...
  • Gunnar Bernstein
    Gunnar Bernstein about 9 years
    Argh. Was just the unhandled exception for no such method. Was chatching something else, so I thought I had it.
  • Farid
    Farid over 4 years
    @GermanK If this was the case, I wonder why did you accept the answer. It will throw the same exception if you haven't declared a constructor with int parameter type
  • GermanK
    GermanK over 4 years
    @Farid I guess the difference was about the parameters, but who remembers after 9 years :)