javainterfacejavabeansjava-8propertydescriptor

Default method in interface in Java 8 and Bean Info Introspector


I have a little problem with default methods in Interface and BeanInfo Introspector. In this example, there is interface: Interface

public static interface Interface {
    default public String getLetter() {
        return "A";
    }
}

and two classes ClassA and ClassB:

public static class ClassA implements Interface {
}

public static class ClassB implements Interface {
    public String getLetter() {
        return "B";
    }
}

In main method app prints PropertyDescriptors from BeanInfo:

public static String formatData(PropertyDescriptor[] pds) {
    return Arrays.asList(pds).stream()
            .map((pd) -> pd.getName()).collect(Collectors.joining(", "));

}

public static void main(String[] args) {


    try {
        System.out.println(
                formatData(Introspector.getBeanInfo(ClassA.class)
                        .getPropertyDescriptors()));
        System.out.println(
                formatData(Introspector.getBeanInfo(ClassB.class)
                        .getPropertyDescriptors()));
    } catch (IntrospectionException e) {
        e.printStackTrace();
    }

}

And the result is:

class
class, letter

Why default method "letter" is not visible as property in ClassA? Is it bug or feature?


Solution

  • We noticed the same problem, particularly with Java EL and JPA. This is likely to affect multiple frameworks that use Introspector to discover properties that follow the JavaBean convention.

    There is an official Bug opened. I thought I would add this for reference since it is creating problems for multiple frameworks.

    The official Fix version is 21. I asked if it could possibly be backported to 17.

    EDIT

    More Info for EL specific issues: https://github.com/jakartaee/expression-language/issues/43

    There may be at least a partial fix in Jakarta EE 10.

    However, there is a fully working workaround for EE < 10 and JDK < 21. You can simply make a BeanInfo class that describes the properties inherited from the interface. Here, MyClass is inheriting aProperty getter/setters from an interface. To expose these to the JDK Introspector, simply create a BeanInfo class:

    public class MyClassBeanInfo extends SimpleBeanInfo {
    
        @Override
        public BeanInfo[] getAdditionalBeanInfo() {
            return new BeanInfo[] { new SimpleBeanInfo() {
                @Override
                public PropertyDescriptor[] getPropertyDescriptors() {
                    try {
                        return new PropertyDescriptor[] { new PropertyDescriptor("aProperty", MyClass.class, "getAProperty", "setAProperty") };
                    } catch (final IntrospectionException e) {
                        throw new RuntimeException(e);
                    }
                }
            } };
        }
    }
    

    Reference: https://docs.oracle.com/javase/8/docs/api/java/beans/BeanInfo.html