我对 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
为什么默认方法“字母”在 ClassA 中作为属性不可见?是错误还是功能?
我猜,
Introspector
不处理 interface
层次结构链,即使使用 Java 8 虚拟扩展方法(又名防御者,默认方法)接口可以有一些有点像属性方法的东西。这是一个相当简单的内省器,声称它确实如此:BeanIntrospector
这是否可以被认为是一个错误是一个灰色地带,这就是我这么认为的原因。
显然,现在一个类可以从一个接口“继承”一个方法,该方法具有官方认为的 getter/setter/mutator 的所有特性。但与此同时,这整个事情都违背了接口的目的——接口不可能提供任何可以被视为属性的东西,因为它是无状态和无行为的,它只是 describe 行为。即使是防御者方法也基本上是静态的,除非它们访问具体实现的real属性。
另一方面,如果我们假设防御者是正式的inherited(而不是providing default implementation这是一个相当模糊的定义),他们应该导致在实现类中创建合成方法,并且那些属于类并作为
PropertyDescriptor
查找的一部分遍历。 显然这不是它的方式,否则整个事情就会奏效。 :) 防御者方法似乎在这里得到了某种特殊待遇。
调试发现这个方法在
Introspector#getPublicDeclaredMethods()
处被过滤掉了:
if (!method.getDeclaringClass().equals(clz)) {
result[i] = null; // ignore methods declared elsewhere
}
其中
clz
是相关类的完全限定名称。
由于
ClassB
有这个方法的自定义实现,所以它成功通过了检查,而ClassA
没有。
我也认为这是一个错误。 您可以为您的班级使用专用的 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上。然而,这对我来说听起来像是一个错误,因为我希望该属性出现在列表中。我怀疑 Inrospector 还没有赶上 Java 8 的特性。
有一个官方Bug被揭开。我想我会添加这个以供参考,因为它给多个框架带来了问题。
官方Fix版本是21,我问是否可以移植到17.