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

kcugc4gi  于 2023-05-12  发布在  Java
关注(0)|答案(5)|浏览(165)

我对Interface和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";
    }
}

在main方法中,app打印来自BeanInfo的PropertyDescriptor:

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还是feature?

vulvrdjw

vulvrdjw1#

我猜,Introspector不处理interface层次链,即使使用Java 8虚拟扩展方法(也称为防御者,默认方法),接口可以有一些看起来有点像属性方法的东西。这里有一个相当简单的内省者,声称它是这样的:BeanIntrospector
这是否可以被认为是一个错误是一个灰色地带,这就是为什么我这么认为。
显然,现在一个类可以从一个接口“继承”一个方法,该方法具有官方认为的getter/setter/mutator的所有特性。但与此同时,这一切都违背了接口的目的--接口不可能提供任何可以被视为属性的东西,因为它是无状态和无行为的,它只是为了描述行为。即使是防御者方法也基本上是静态的,除非它们访问具体实现的 * 真实的 * 属性。
另一方面,如果我们假设防御者是正式的 * 继承 (而不是 * 提供默认实现 ,这是一个相当模糊的定义),它们应该导致在实现类中创建合成方法,并且这些方法属于类并作为PropertyDescriptor查找的一部分被遍历。 显然这不是它的方式,虽然,否则整个事情将工作。:) 似乎防守方的方法在这里得到了某种特殊的待遇。

lymgl2op

lymgl2op2#

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

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

其中clz是所讨论的类的完全限定名。
由于ClassB有这个方法的自定义实现,所以它成功通过了检查,而ClassA没有。

rks48beu

rks48beu3#

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

pcrecxhr4#

我们注意到了同样的问题,特别是Java EL和JPA。这可能会影响使用Introspector来发现遵循JavaBean约定的属性的多个框架。
有一个正式的Bug打开了。我想我会添加这一点作为参考,因为它正在为多个框架制造问题。

官方版本是21。我问他是否有可能回到17。

编辑

有关EL特定问题的更多信息:https://github.com/jakartaee/expression-language/issues/43
在Jakarta EE 10中可能存在至少部分修复。
但是,对于EE < 10和JDK < 21,有一个完全可行的解决方案。您可以简单地创建一个BeanInfo类来描述从接口继承的属性。这里,MyClass从接口继承aProperty getter/setter。要向JDK Introspector公开这些,只需创建一个BeanInfo类:

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

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

j2cgzkjk

j2cgzkjk5#

这是因为你只有接口和ClassB上的方法,而不是直接在ClassA上。然而,这听起来像一个错误,因为我希望该属性显示在列表中。我怀疑Inrospector还没有赶上Java 8的功能。

相关问题