Java 8 和 Bean 信息自省函数中接口中的默认方法

我对界面和BeanInfo Introspector中的默认方法有一点问题。在此示例中,有接口:接口

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

以及两个类 A 类和 B 类:

public static class ClassA implements Interface {
}

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

在 main 方法应用中,从 BeanInfo 打印 PropertyDescriptors:

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

为什么默认方法“letter”在 ClassA 中不可见为属性?是错误还是功能?


答案 1

我猜,不处理层次结构链,即使使用Java 8虚拟扩展方法(又名防御者,默认方法)接口可以具有类似于属性方法的东西。这是一个相当简单的内省者,声称它确实如此:BeanIntrospectorIntrospectorinterface

这是否可以被认为是一个错误在某种程度上是一个灰色地带,这就是为什么我这么认为。

显然,现在一个类可以从接口“继承”一个方法,该方法具有通常被认为是 getter/setter/mutator 的所有品质。但与此同时,这整件事违背了接口的目的——接口不可能提供任何可以被认为是属性的东西,因为它是无状态和无行为的,它只是为了描述行为。即使是防御者方法基本上也是静态的,除非它们访问具体实现的实际属性。

另一方面,如果我们假设防御者是正式继承的(而不是提供默认实现,这是一个相当模糊的定义),它们应该导致在实现类中创建合成方法,并且这些方法属于该类并作为查找的一部分进行遍历。显然,事实并非如此,否则整个事情就会起作用。:)似乎防御方法在这里得到了某种特殊对待。PropertyDescriptor


答案 2

调试显示此方法在 以下位置被过滤掉:Introspector#getPublicDeclaredMethods()

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

其中 是相关类的完全限定名称。clz

由于具有此方法的自定义实现,因此它成功通过检查,而没有。ClassBClassA