56

I need to invoke the setter methods of a class using reflection, and the code is as below:

try {             
   Method method = myObj.getClass().getMethod("set" + fieldName, new Class[] { value.getClass() });               
   method.invoke(myObj, value);
     } catch (Exception ex) {
         ex.printStackTrace();
     }

The value is an ArrayList and the setter method is as below:

public void setNames(List<String> names){
    this.names = names;
}

A java.lang.NoSuchMethodException is thrown when running this code, but when the setter method parameter type is changed to ArrayList from List it executes fine. Is there a way to keep the setter method parameter in super type and still use reflection without manually giving the type of the parameter when getting the method from the class?

Dilini Rajapaksha
  • 2,610
  • 4
  • 30
  • 41

7 Answers7

61

If you happen to use spring framework, you could use the PropertyAccessorFactory for retrieving an implementation of the PropertyAccessor interface:

Accessing properties directly

PropertyAccessor myAccessor = PropertyAccessorFactory.forDirectFieldAccess(object);
// set the property directly, bypassing the mutator (if any)
myAccessor.setPropertyValue("someProperty", "some value");

Accessing properties through accessors/mutators

If you need to access your properties using their getters and setters, you could use instead the forBeanPropertyAccess method:

PropertyAccessor myAccessor = PropertyAccessorFactory.forBeanPropertyAccess(object);
// a `setSomeProperty()` method will be used
myAccessor.setPropertyValue("someProperty", "some value");
el.atomo
  • 5,200
  • 3
  • 30
  • 28
60

You could use BeanUtils:

Step #1

Customer customer = new Customer();

Step #2

BeanUtils.setProperty(customer,"firstName","Paul Young");

You could iterate all class members using reflection and set values accordingly, assuming customer object has:

private String firstName;
// Getter and Setter are defined
Nayn
  • 3,594
  • 8
  • 38
  • 48
Anand
  • 1,845
  • 2
  • 20
  • 25
29

Contrary to other answers, there is a really simple solution. See java.beans.Statement. It gives you a way to execute arbitrary reflective code without having to worry about actual vs formal types (and a few other things).

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Thanks for the simple solution, I tried it and it works perfectly. I was not aware of this `java.beans.Statement` class and did some referencing. It is very hard to find many articles on this except for the API doc. In [this](http://ainthek.blogspot.com/2010/08/raw-reflection-vs-javabeansexpression.html) article it compares with the reflection API and says it is slow. Is there any reason why it is not much popular like the reflection API? – Dilini Rajapaksha Apr 04 '12 at 10:46
  • 2
    Looking at the sources of `java.beans.Statement`, its slowness is explained by that it handles all those details such as access modifiers, most specific polymorphic match etc. It's a tradeoff - do you want simplicity at the cost of performance, or performance at the cost of making some assumptions (which, if not correct, can be dangerous). – Joonas Pulakka Apr 04 '12 at 11:06
  • Thanks @JoonasPulakka. I'd choose simplicity over performence, since it is not a huge effect as yet. – Dilini Rajapaksha Apr 04 '12 at 11:38
  • 1
    @JoonasPulakka It's slow because it does all the things you need to do to solve this problem. Resolving actual argument types to formal paramater types is O(M**N) if M is the number of parameters and N the length of their type inheritance chain. – user207421 Apr 04 '12 at 23:28
5

There is a simple solution, but that simplicity comes at the cost of performance.

I'm using this monster instead:

public static Method findMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {

    // First try the trivial approach. This works usually, but not always.
    try {
        return clazz.getMethod(methodName, parameterTypes);
    } catch (NoSuchMethodException ex) {
    }

    // Then loop through all available methods, checking them one by one.
    for (Method method : clazz.getMethods()) {

        String name = method.getName();
        if (!methodName.equals(name)) { // The method must have right name.
            continue;
        }

        Class<?>[] acceptedParameterTypes = method.getParameterTypes();
        if (acceptedParameterTypes.length != parameterTypes.length) { // Must have right number of parameters.
            continue;
        }

        boolean match = true;
        for (int i = 0; i < acceptedParameterTypes.length; i++) { // All parameters must be right type.
            if (null != parameterTypes[i] && !acceptedParameterTypes[i].isAssignableFrom(parameterTypes[i])) {
                match = false;
                break;
            }
            if (null == parameterTypes[i] && acceptedParameterTypes[i].isPrimitive()) { // Accept null except for primitive fields.
                match = false;
                break;
            }
        }

        if (match) {
            return method;
        }

    }

    // None of our trials was successful!
    throw new NoSuchMethodException();
}

parameterTypes are what you get from your value.getClass(). Some or all of them can be also null. Then they are treated as matces for any non-primitive parameter fields.

Even this isn't quit perfect: If there are several methods that are polymorphically suitable but none of which matches exactly, then the returned method is chosen arbitrarily (the first match in the array that clazz.getMethods() returns is taken). This behavior differs from Java the Language behavior, in which the "closest match" is always used.

If getting the method by name is sufficient (i.e. you assume that the parameters are suitable if the name matches), then you can manage with much simpler (and somewhat faster):

public static Method findMethod(Class<?> clazz, String methodName) {
  for (Method method : clazz.getMethods()) {
    if (method.getName().equals(methodName)) {
      return method;
    }
  }
  throw new NoSuchMethodException();
} 

To further boost it up, consider some sort of cache.

Community
  • 1
  • 1
Joonas Pulakka
  • 36,252
  • 29
  • 106
  • 169
4

Reflection Class is sometimes called as dynamic invocation.

for example, lets look around getter methods using reflection

consider there is a class called MyReflectionClass and has a method called getreflect(). (eg type string) lets see how to use reflection classes

MyReflectionClass  obj = new MyReflectionClass();

<? extends MyReflectionClass> tempClass = obj.getClass();
String a = (String) obj.getMethod("getreflect").invoke(obj);

now setter method

(String) obj.getDeclaredMethod("setreflect", String.class).invoke(obj,"MyString");

if you need to do the same operation with sequence of string then

    (String) obj.getDeclaredMethod("setreflect",
 new String.class{}).invoke(obj,"MyString1","MyString2");

hope it may be useful

Derlin
  • 9,572
  • 2
  • 32
  • 53
1

Example set All filds using the setters methods geting the values with ResultSet.

private Object setAllSetters(Object ob, ResultSet rs) throws SQLException{
    // MZ: Find the correct method
    Class cls = ob.getClass();
    while (rs.next()) {
        for (Field field : cls.getDeclaredFields()){
            for (Method method : cls.getMethods())
            {
                if ((method.getName().startsWith("set")) && (method.getName().length() == (field.getName().length() + 3)))
                {
                    if (method.getName().toLowerCase().endsWith(field.getName().toLowerCase()))
                    {
                        // MZ: Method found, run it
                        try
                        {
                            method.setAccessible(true);
                            if(field.getType().getSimpleName().toLowerCase().endsWith("integer"))
                                method.invoke(ob,rs.getInt(field.getName().toLowerCase()));
                            else if(field.getType().getSimpleName().toLowerCase().endsWith("long"))
                                method.invoke(ob,rs.getLong(field.getName().toLowerCase()));
                            else if(field.getType().getSimpleName().toLowerCase().endsWith("string"))
                                method.invoke(ob,rs.getString(field.getName().toLowerCase()));
                            else if(field.getType().getSimpleName().toLowerCase().endsWith("boolean"))
                                method.invoke(ob,rs.getBoolean(field.getName().toLowerCase()));
                            else if(field.getType().getSimpleName().toLowerCase().endsWith("timestamp"))
                                method.invoke(ob,rs.getTimestamp(field.getName().toLowerCase()));
                            else if(field.getType().getSimpleName().toLowerCase().endsWith("date"))
                                method.invoke(ob,rs.getDate(field.getName().toLowerCase()));
                            else if(field.getType().getSimpleName().toLowerCase().endsWith("double"))
                                method.invoke(ob,rs.getDouble(field.getName().toLowerCase()));
                            else if(field.getType().getSimpleName().toLowerCase().endsWith("float"))
                                method.invoke(ob,rs.getFloat(field.getName().toLowerCase()));
                            else if(field.getType().getSimpleName().toLowerCase().endsWith("time"))
                                method.invoke(ob,rs.getTime(field.getName().toLowerCase()));
                            else 
                                method.invoke(ob,rs.getObject(field.getName().toLowerCase()));
                        }
                        catch (IllegalAccessException | InvocationTargetException | SQLException e)
                        {
                            System.err.println(e.getMessage());
                        }
                    }
                }
            }
        }
    }
    return ob;
}
0

Detecting Method Names using String Handling might not look like right way of doing it. Consider this as one of the solutions.

    try {
            Animal animal = new Animal();
            BeanInfo beaninfo = Introspector.getBeanInfo(Animal.class);
            PropertyDescriptor pds[] = beaninfo.getPropertyDescriptors();
            Method setterMethod=null;
            for(PropertyDescriptor pd : pds) { 
                setterMethod = pd.getWriteMethod(); // For Setter Method

           /*
               You can get Various property of Classes you want. 
           */

                System.out.println(pd.getName().toString()+ "--> "+pd.getPropertyType().toString()+"--Setter Method:->"+pd.getWriteMethod().toString());

                if(setterMethod == null) continue;
                else
                    setterMethod.invoke(animal, "<value>");
            }
        }catch(Exception e) {e.printStackTrace();}
Mitesh Manani
  • 85
  • 1
  • 8