Java 8和Bean Info Introspector中接口的默认方法

我对Interface和BeanInfo Introspector中的默认方法有一点问题。 在这个例子中,有接口:Interface

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"; } } 

在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中不可见为属性? 是bug还是function?

我猜, Introspector不会处理interface层次结构链,即使使用Java 8虚拟扩展方法(也称为防御者,默认方法)接口可以使某些东西看起来像属性方法。 这是一个相当简单的内省探测器声称它: BeanIntrospector

这是否可以被认为是一个灰色区域,这就是为什么我这么认为。

显然,现在一个类可以从一个接口“inheritance”一个方法,该方法具有通常被认为是getter / setter / mutator的所有特性。 但与此同时,这一切都违背了接口的目的 – 接口不可能提供任何可被视为属性的东西,因为它是无状态且无行为的,它只是用来描述行为。 甚至防御方法基本上都是静态的,除非它们访问具体实现的真实属性。

另一方面,如果我们假设防御者是正式inheritance的 (而不是提供默认实现 ,这在某种程度上是一个不明确的定义),它们应该导致在实现类中创建合成方法,并且那些属于类并且遍历作为PropertyDescriptor查找的一部分。 显然这不是它的方式,否则整个事情都会起作用。 🙂似乎防守方法在这里得到某种特殊待遇。

我认为这也是一个错误。 你可以使用专门的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; } 

这是因为您只在Interface和ClassB上使用您的方法,而不是直接在ClassA上。 然而,这听起来像是一个bug,因为我希望该属性能够在列表中显示出来。 我怀疑Inrospector还没有赶上Java 8的function。

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

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

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

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