10

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".
Tunaki
  • 132,869
  • 46
  • 340
  • 423
Quirexx
  • 173
  • 3
  • 15
  • What happens if you declare the `isTest` method at the end of the class? It might be that it is recognized as boolean because that is the first occurrence, and thus the setter does not match with type String. – Tamas Hegedus Sep 08 '15 at 19:17
  • No, it does not make sense, but apparently that's what they decided (or it's a bug). You can't do much about this, except stopping to represent booleans with strings :D – Dici Sep 08 '15 at 19:24
  • I added some more info in the initial text. The order does not have an effect on the outcome. – Quirexx Sep 08 '15 at 19:30

2 Answers2

4

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

}
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • I agree with Tunaki, and if you are doing some metaprogramming, you can force it to work as you wanted with some parameters: `PropertyDescriptor descr = new PropertyDescriptor("test", Arguments.class, "getTest", "setTest");` – Ale Zalazar Sep 08 '15 at 19:42
  • You mention "it uses isTest to determine the type of the test property as boolean", but the type of test is clearly defined as String. Or is it defining the type as Boolean because the return value of the method is Boolean? – Quirexx Sep 08 '15 at 19:49
  • @Quirexx The Introspector does not read the fields. It uses the signature of the getter / setter method to determine which fields are present and their corresponding types. `isTest` signature specifies for a property named "test" of type boolean, regardless of the actual type of "test" – Tunaki Sep 08 '15 at 19:51
  • So it only looks at the methods and follows the JavaBeans naming convention to determine the properties. If you look at it that way, the result is indeed correct. Now it makes sense. Thanks! – Quirexx Sep 08 '15 at 20:00
2

The actual member is completely irrelevant for the Introspector. You can, for example, have a getName() method that only returns a fixed String and the Introspector will find it to be the getter for a member called "name". You can even have a setter if the member does not exist. You can even give an Interface to the Introspector and it will determine the properties from that, even if there cannot be any real members.

In other words, the properties are determined by the existence of getter and setter methods and not by actually searching for variables.

Florian Schaetz
  • 10,454
  • 5
  • 32
  • 58