Метод по умолчанию в интерфейсе в Java 8 и Bean Info Introspector

У меня небольшая проблема с методами по умолчанию в интерфейсе и BeanInfo Introspector. В этом примере есть интерфейс: Интерфейс

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

и два класса ClassA и ClassB:

public static class ClassA implements Interface {
}

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

В главном методе приложение печатает PropertyDescriptors из 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();
    }

}

И результат:

class
class, letter

Почему метод "письмо" по умолчанию не отображается как свойство в ClassA? Это ошибка или функция?

Ответ 1

Я думаю, Introspector не обрабатывает цепочки иерархии interface, хотя при использовании методов виртуального расширения Java 8 (например, защитников, методов по умолчанию) могут иметь что-то, что sorta выглядит как методы свойств. Здесь довольно упрощенный интроспектор, который утверждает, что он делает: BeanIntrospector

Можно ли считать, что ошибка является серой областью, вот почему я так думаю.

Очевидно, что теперь класс может "наследовать" из интерфейса метод, который обладает всеми качествами того, что официально рассматривается как getter/setter/mutator. Но в то же время все это связано с интерфейсом - интерфейс не может обеспечить ничего, что можно считать собственностью, поскольку оно без гражданства и безболезненное, оно предназначено только для описания поведения. Даже методы защитника в основном статичны, если они не имеют доступа к реальным свойствам конкретной реализации.

С другой стороны, если мы предполагаем, что защитники официально унаследованы (в отличие от предоставления реализации по умолчанию, которая является нечетким определением), они должны приводить к синтетическим методам, создаваемым в классе реализации, и те, которые принадлежат классу и пересекаются как часть поиска PropertyDescriptor. Очевидно, это не так, как есть, иначе все будет работать.:) Кажется, что методы защитника получают какое-то особое лечение здесь.

Ответ 2

Я тоже думаю, что это ошибка. Вы можете решить это, используя выделенный BeanInfo для своего класса, и предоставив что-то вроде этого:

/* (non-Javadoc)
 * @see java.beans.SimpleBeanInfo#getAdditionalBeanInfo()
 */
@Override
public BeanInfo[] getAdditionalBeanInfo()
{
    Class<?> superclass = Interface.class;
    BeanInfo info = null;

    try
    {
        info = Introspector.getBeanInfo(superclass);
    }
    catch (IntrospectionException e)
    {
        //nothing to do
    }

    if (info != null)
        return new BeanInfo[] { info };

    return null;
}

Ответ 3

Это потому, что у вас есть только ваш метод в интерфейсе и ClassB, а не на ClassA напрямую. Однако это звучит для меня как ошибка, так как я ожидаю, что это свойство появится в списке. Я подозреваю, что Inrospector еще не догнал возможности Java 8.

Ответ 4

Отладка показывает, что этот метод отфильтровывается в Introspector#getPublicDeclaredMethods():

if (!method.getDeclaringClass().equals(clz)) {
    result[i] = null; // ignore methods declared elsewhere
}

где clz - это полностью квалифицированное имя рассматриваемого класса.

Так как ClassB имеет пользовательскую реализацию этого метода, он успешно проходит проверку, а ClassA - нет.