Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Introspection - weird behaviour

The code below is a small example that easily reproduces the issue. So I have variable of type String, on which a default value is set. I have 3 methods:

  • getter
  • setter
  • convenience method that converts the string to boolean

The introspection does not return the getter as the readMethod and the setter as the writeMethod. Instead it returns the isTest() method as the readMethod. The setter is empty.

From the documentation I understand that if the type would be a boolean, the "is" method has higher precedence over get, but the type is String, so it does not make sense to even look for an "is-xxx" method?

public class Test {
    public class Arguments {
        private String test = Boolean.toString(true);

        public boolean isTest() {
            return Boolean.parseBoolean(test);
        }

        public String getTest() {
            return test;
        }

        public void setTest(String test) {
            this.test = test;
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IntrospectionException {
        BeanInfo info = Introspector.getBeanInfo(Arguments.class);
        System.out.println("Getter: " + info.getPropertyDescriptors()[1].getReadMethod());
        System.out.println("Setter: " + info.getPropertyDescriptors()[1].getWriteMethod());
        PropertyDescriptor descr = new PropertyDescriptor("test", Arguments.class);
        System.out.println("T");
    }

}

Is there anyone who has some insight on this?

Additional information:

  1. The order does not change the outcome. The isTest() method is always seen as readMethod
  2. In case I simply rename the isTest() to bsTest(), it selects the getter and setter as readMethod and writeMethod. So it has something to do with "is-xxx".
like image 750
Quirexx Avatar asked Sep 08 '15 19:09

Quirexx


1 Answers

The result you are getting is actually the expected result, as per the JavaBeans specification.

Quoting paragraph 8.3.1 for simple properties:

If we discover a matching pair of get<PropertyName> and set<PropertyName> methods that take and return the same type, then we regard these methods as defining a read-write property whose name will be <propertyName>.

Then, quoting paragraph 8.3.2 for boolean properties:

This is<PropertyName> method may be provided instead of a get<PropertyName> method, or it may be provided in addition to a get<PropertyName> method.

In either case, if the is<PropertyName> method is present for a boolean property then we will use the is<PropertyName> method to read the property value.

From your example, the Introspector is detecting both the isTest and getTest method. Since isTest has priority over getTest, it uses isTest to determine the type of the test property as boolean. But then, the Introspector expects the setter to have the signature void setTest(boolean test) and it doesn't find it, so the setter method is null.

What's important to note is that the Introspector does not read the fields. It uses the signature of the getter / setter methods to determine which fields are present and their corresponding types. isTest method signature specifies for a property named test of type boolean, so, regardless of the actual type of test, the Introspector will consider that your class has a property boolean test.

In fact, for all the Introspecter is concerned, the property test might not even exist! You can convince yourself of that with the following code:

class Test {

    public class Arguments {
        public boolean isTest() {
            return true;
        }
    }

    public static void main(String[] args) throws IntrospectionException {
        BeanInfo info = Introspector.getBeanInfo(Arguments.class);
        System.out.println("Getter: " + info.getPropertyDescriptors()[1].getReadMethod());
        System.out.println("Name of property: " + info.getPropertyDescriptors()[1].getName());
    }

}
like image 176
Tunaki Avatar answered Oct 12 '22 16:10

Tunaki