По умолчанию метод интерфейса Java 8 не объявляет свойство
В моем приложении я столкнулся с проблемой, заключающейся в том, что, когда метод получения по умолчанию в классе используется по умолчанию только в интерфейсе (функция Java 8), в результате отсутствует свойство Java Beans. Т.е. для обычного вызова метода он работает как стандартный метод, но для доступа через "свойства" он вдруг ведет себя иначе...
Вот тестовый пример:
import java.beans.Introspector;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.apache.commons.beanutils.PropertyUtils;
public class test
{
public static void main (String[] arguments) throws Exception
{
// Normal language-level invocation, works fine.
System.out.println (new Bean1 ().getFoo ());
System.out.println (new Bean2 ().getFoo ());
// Printing Java Beans properties; Bean2 doesn't have 'foo' property...
System.out.println (Arrays.stream (Introspector.getBeanInfo (Bean1.class).getPropertyDescriptors ())
.map ((property) -> property.getName ())
.collect (Collectors.joining (", ")));
System.out.println (Arrays.stream (Introspector.getBeanInfo (Bean2.class).getPropertyDescriptors ())
.map ((property) -> property.getName ())
.collect (Collectors.joining (", ")));
// First call behaves as expected, second dies with exception.
System.out.println (PropertyUtils.getProperty (new Bean1 (), "foo"));
System.out.println (PropertyUtils.getProperty (new Bean2 (), "foo"));
}
public interface Foo
{
default String getFoo ()
{
return "default foo";
}
}
public static class Bean1 implements Foo
{
@Override
public String getFoo ()
{
return "special foo";
}
}
public static class Bean2 implements Foo
{ }
}
Результат:
special foo
default foo
class, foo
class
special foo
Exception in thread "main" java.lang.NoSuchMethodException: Unknown property 'foo' on class 'class test$Bean2'
at org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(PropertyUtilsBean.java:1257)
at org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:808)
at org.apache.commons.beanutils.PropertyUtilsBean.getProperty(PropertyUtilsBean.java:884)
at org.apache.commons.beanutils.PropertyUtils.getProperty(PropertyUtils.java:464)
at test.main(test.java:21)
Вопросы: я делаю что-то не так или это ошибка в Java? Есть ли обходной путь, отличный от использования методов по умолчанию (для методов получения / установки), если вам может понадобиться доступ к ним как к "свойству" в какой-то момент позже?
Я всегда ненавидел Java "свойства по соглашению", которые имеют тенденцию ломаться, потому что вы чихаете не так.
3 ответа
Похоже, это действительно ошибочное упущение в Бобах Introspector
, Вот обходной путь, отличный от использования default
методы:
public static void main (String[] arguments) throws Exception {
testBean(new Bean1());
System.out.println();
testBean(new Bean2());
}
static void testBean(Object bean) throws Exception {
PropertyDescriptor[] pd
= Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
System.out.println(Arrays.stream(pd)
.map(PropertyDescriptor::getName).collect(Collectors.joining(", ")));
for(PropertyDescriptor p: pd)
System.out.println(p.getDisplayName()+": "+p.getReadMethod().invoke(bean));
}
public interface Foo {
default String getFoo() {
return "default foo";
}
}
public static class Bean1 implements Foo {
@Override
public String getFoo() {
return "special foo";
}
}
public static class Bean2BeanInfo extends SimpleBeanInfo {
private final BeanInfo ifBeanInfo;
public Bean2BeanInfo() throws IntrospectionException {
ifBeanInfo=Introspector.getBeanInfo(Foo.class);
}
@Override
public BeanInfo[] getAdditionalBeanInfo() {
return new BeanInfo[]{ifBeanInfo};
}
}
public static class Bean2 implements Foo { }
class, foo
class: class helper.PropTest$Bean1
foo: special foo
class, foo
class: class helper.PropTest$Bean2
foo: default foo
Я не знаю, будет ли мой ответ полезным, но я решил похожую проблему, используя BeanUtils.getPropertyDescriptors(clazz)
из весны. Он понимает методы по умолчанию.
Быстрый обходной путь:
try {
return PropertyUtils.getProperty(bean, property);
}
catch (NoSuchMethodException e) {
String getMethod = "get" + property.substring(0, 1).toUpperCase() + property.substring(1);
return MethodUtils.invokeMethod(bean, getMethod, new Object[]{});
}