views:

81

answers:

3

Here is some code I've been twiddling with to try and lazily fill in fields in object, mostly for object factories in JUnit but it could be quite a useful method to have.

    private void lazyObjectFill(Object profil) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
    final Method[] list = profil.getClass().getDeclaredMethods();
    for (Method m : list) {
        if (Modifier.isPublic(m.getModifiers()) && m.getName().startsWith("set")) {

            final Class< ?>[] parameterTypes = m.getParameterTypes();
            if (parameterTypes.length == 1) {
                final Class< ?> clazz = parameterTypes[0];
                if (clazz == String.class) {
                    log.info("Invoking " + m.getName() + " with [\"\"]");
                    m.invoke("");
                } else if (clazz.isPrimitive() && Defaults.defaultValue(clazz) != null) {
                    log.info("Invoking " + m.getName() + " with [" + Defaults.defaultValue(clazz) + "]");
                    m.invoke(Defaults.defaultValue(clazz));
                }
            }

        }
    }
}

We get the following exception when running this code on an object.

java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:592)

The exception occurs when running m.invoke(""); on a string setter.


Updated source code for the benefit of the googlers.

private void lazyObjectFill(Object obj) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
    final Method[] list = obj.getClass().getDeclaredMethods();
    for (Method method : list) {
        method.setAccessible(true);
        if (Modifier.isPublic(method.getModifiers()) && method.getName().startsWith("set")) {

            final Class< ?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                final Class< ?> clazz = parameterTypes[0];
                if (clazz == String.class) {
                    log.info("Invoking " + method.getName() + " with [\"\"]");
                    method.invoke(obj, "");
                } else if (clazz.isPrimitive() && Defaults.defaultValue(clazz) != null) {
                    log.info("Invoking " + method.getName() + " with [" + Defaults.defaultValue(clazz) + "]");
                    method.invoke(obj, Defaults.defaultValue(clazz));
                }
            }

        }
    }
}
+3  A: 

You are almost there, but methods are static and they need an object on which to invoke them.

i.e.

m.invoke(profil, "");

and

m.invoke(profil, Defaults.defaultValue(clazz));

You were (unknowingly) trying to execute the method on a string object without parameters. And since the string class does not have that method, it had to fail. Details can be found in the Method javadoc.

BTW: Static methods are invoked like this:

method.invoke(null, params);
seanizer
Yup that's correct. It actually dawned on me moments after posting this in a real face-palm. Thanks for a rapid reply!
BjornS
What do you mean by methods are static?
mR_fr0g
@mR_fr0g He means that `Method` objects are static in that they refer to a particular method on a class (which could itself be static or not) but they aren't tied to any _instance_ of that class. Thus, an instance has to be provided to invoke a `Method` that refers to a non-static method.
ColinD
@ColinD exactly. as opposed to languages like javascript where this kind of thing works: `var myMethod = myObj.methodName; myMethod(param1, param2)` (invoked on object myObj)
seanizer
+2  A: 

You know that invoke method of Method takes two arguments ? As a consequence, I guess you wrote

m.invoke(profil, "")

Besides, I would personnally not have separated String from other objects.

And finally, to correctly identify obejct fields, i would have preferred a mixed approach

  1. Use an enumeration of non static Field members
  2. Use BeanInfo to access bean properties.
Riduidel
BjornS
Yes, BeanInfo is definitely the way to go for identifying setters (+1)
seanizer
+1  A: 

The first parameter of the Method.invoke() is the object which will invoke the method.

For example in your case m.invoke(profil, ""); or m.invoke(profil, Defaults.defaultValue(clazz));

Colin Hebert