0

I have a Field f of some class MyClass and I want to retrieve the associated getter/setter, if it exists. How do I do this?

  • I checked the methods of Field, but there is not even one that returns a Method.
  • I played around with BeanInfo retrieved by Introspector.getBeanInfo(Class<?> beanClass). It has a method getPropertyDescriptors() which returns an array of PropertyDescriptor, and each PropertyDescriptor allows retrieving the getters and setters via getReadMethod() and getWriteMethod(). But there seems to be no connection to a Field. Also, it seems impossible to get a certain PropertyDescriptor (e.g. retrieved by giving a field name as argument); so even IF there is a way to get the Field from the PropertyDescriptor, I would need to loop through all PropertyDescriptors. This is not performant.

NOTE: I do not want to rely on naming conventions, so please do not give anwers that fiddle with the field name. A getter of a field myField that does not have the name getMyField or isMyField is still a getter, after all.

Kjara
  • 2,504
  • 15
  • 42
  • 3
    A getter for a field is based on **naming conventions** (standard or spec or whatever that can be called, JavaBeans in this specific case is just a convention). Even if you have a tool that gives a a direct `field -> getter` link, it's just a shortcut around names – ernest_k Aug 14 '18 at 09:14
  • 2
    I very much doubt you can do this without relying on naming conventions (which are, after all, the only way **we** know they're getters/setters for a given field). You'd have to introspect the **code** of every method in the class accepting a single argument of that field's type or returning that type. And even then, how could you know they were just setters/getters as opposed to having some other purpose? Validation code (in a setter) or lazy-initialization code (in a getter) looks a lot like code doing something else, after all... – T.J. Crowder Aug 14 '18 at 09:15
  • The idea is that getters and setters may not directly correspond to an actual field, they may be derived, calculated or stored elsewhere. In other words there will not necessarily be a direct correspondence, so Java doesn't have anything for that. – Mark Rotteveel Aug 14 '18 at 12:41

3 Answers3

0

This is my idea, not totally correct though. Check if the method name has the field name inside it, plus return type and parameter argument type. I feel if no rules about setter/getter method of field. It is almost impossible.

    @Test
    void test_fieldGetterSetter() throws NoSuchFieldException {
    Field f = MyClass.class.getDeclaredField("name");
    Class<?> declaringClass = f.getDeclaringClass();

    Method[] declaredMethods = declaringClass.getDeclaredMethods();

    for (Method declaredMethod : declaredMethods) {
        String name = declaredMethod.getName();

        // Can also check return type, argument type to increase the correctness
        if (isFieldNameWithMethodName(f.getName(), name)) {
            String format = String.format("Field name is %s and possible method name is %s", f.getName(), name);
            System.out.println(format);
        }
    }
}

private boolean isFieldNameWithMethodName(String fieldName, String methodName) {
    if (methodName.toLowerCase().contains(fieldName.toLowerCase())) {
        return true;
    }

    return false;
}
sendon1982
  • 9,982
  • 61
  • 44
-1

Turned out that in Java, getters/setters are not just defined by their signature and behaviour (as I thought) but ALSO by following naming conventions.

So in order to get a getter/setter for a Field myField of object myObject, we need to create its corresponding getter/setter method name methodName (use Field.getName() then fiddle with get/set and camelCase), then call myObject.getClass().getMethod(methodName, ...). The parameters (...) can be retrieved by using Field.getType() and must be different according to whether I want the getter or setter.

Proof that naming is important: For this class

public class ClassWithUnconventionalNaming {

    private int foo;
    public int myGetter() { return foo; }
    public void mySetter(int x) { this.foo = x; }
    public void setBaz(boolean x) { this.foo = 2; };

    private String bar;
    public String getBar() { return "hello world"; }
    public void setBar(String bar) { this.bar = bar; }

    private boolean baz;
    public boolean isBaz() { return baz; }
}

I ran the code

ClassWithUnconventionalNaming myClass = new ClassWithUnconventionalNaming();
myClass.mySetter(5);

BeanInfo info = Introspector.getBeanInfo(ClassWithUnconventionalNaming.class);
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
    String propertyName = pd.getName();
    System.out.println("property name: " + propertyName);
    Method getter = pd.getReadMethod();
    System.out.println("    getter: " + ((getter == null) ? "" : getter.getName()));
    Method setter = pd.getWriteMethod();
    System.out.println("    setter: " + ((setter == null) ? "" : setter.getName()));
}

and got the output

property name: bar
    getter: getBar
    setter: setBar
property name: baz
    getter: isBaz
    setter: setBaz
property name: class
    getter: getClass
    setter: 
Kjara
  • 2,504
  • 15
  • 42
-1
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ClassUtils {
/**
 * Returns the corresponding getter method for a field from this object
 *
 * @param field
 * @return
 * @throws NoSuchMethodException
 * @throws SecurityException
 */
public static Method getGetterMethodForField(Field field) throws NoSuchMethodException, SecurityException {
    String fieldName = field.getName();
    String getterMethodName = "get" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1);
    try
    {
        return field.getType().getMethod(getterMethodName);
    }catch( NoSuchMethodException e ) {
        // If that fails try with "is" instead
        getterMethodName = "is" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1);
        return field.getType().getMethod(getterMethodName);
    }
}

/**
 * Returns the corresponding getter method for a field from the specified object
 *
 * @param field
 * @return
 * @throws NoSuchMethodException
 * @throws SecurityException
 */
public static Method getGetterMethodForField(Field field, Object obj) throws NoSuchMethodException, SecurityException {
    String fieldName = field.getName();
    String getterMethodName = "get" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1);
    try
    {
        return obj.getClass().getMethod(getterMethodName);
    }catch( NoSuchMethodException e ) {
        // If that fails try with "is" instead
        getterMethodName = "is" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1);
        return obj.getClass().getMethod(getterMethodName);
    }
}

/**
 * Returns the corresponding getter method for a field from the specified class
 *
 * @param field
 * @return
 * @throws NoSuchMethodException
 * @throws SecurityException
 */
public static Method getGetterMethodForField(Field field, Class clazz) throws NoSuchMethodException, SecurityException {
    String fieldName = field.getName();
    String getterMethodName = "get" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1);
    try
    {
        return clazz.getMethod(getterMethodName);
    }catch( NoSuchMethodException e ) {
        // If that fails try with "is" instead
        getterMethodName = "is" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1);
        return clazz.getMethod(getterMethodName);
    }
}


/**
 * Returns the corresponding getter method for a field from this object
 *
 * @param field
 * @return
 * @throws NoSuchMethodException
 * @throws SecurityException
 */
public Method getSetterMethodForField(Field field, Class<?> argType) throws SecurityException, NoSuchMethodException {
    String fieldName = field.getName();
    String setterMethodName = "set" + fieldName.toUpperCase().charAt(0) + fieldName.substring(1);

    do {
        try {
            Method method = this.getClass().getMethod(setterMethodName, argType);
            return method;
        } catch (NoSuchMethodException e) {

            // Check if the argument is a primitive
            if( org.apache.commons.lang3.ClassUtils.wrapperToPrimitive(argType) != null ) {
                argType = org.apache.commons.lang3.ClassUtils.wrapperToPrimitive(argType);
                try {
                    Method method = this.getClass().getMethod(setterMethodName, argType);
                    return method;
                } catch (NoSuchMethodException e2) {
                    argType = argType.getSuperclass();
                }
            }
            else {
                // Check for interfaces
                for( Class<?> baseArgType : org.apache.commons.lang3.ClassUtils.getAllInterfaces(argType) ) {
                    try {
                        Method method = this.getClass().getMethod(setterMethodName, baseArgType);
                        return method;
                    } catch (NoSuchMethodException e2) {
                    }
                }
                argType = null;
            }

        }
    } while (argType != null);

    throw new NoSuchMethodException(setterMethodName);
}

}

Sparm
  • 529
  • 1
  • 6
  • 14