JavaBeanProperties в JavaFX без использования java.desktop (Swing, AWT) - PullRequest
0 голосов
/ 03 декабря 2018

У меня есть несколько классов моделей, которые создаются много раз и имеют много полей.Хотя я мог инициализировать все поля как Simple*Property с, это значительно снижает производительность из-за выделений (и ленивое создание свойств не вариант).

Таким образом, я бы предпочел использовать JavaBeanProperties длясоздайте привязки по требованию, где это необходимо, в приложении, например, так (см. этот ответ для полного примера Обтекание JavaBean со свойствами JavaFX ):

Entity bean = ...
StringProperty nameProperty = JavaBeanStringPropertyBuilder()
  .bean(bean)
  .name("name")
  .build();

Однако я не хочу зависетьна java.desktop и связанных с ним компонентах Swing в моем module-info.java

Я мог бы потенциально переписать желаемую функциональность, установив SimpleIntegerProperty, который увеличивается на каждый из методов set * объекта, а затем добавить прослушиватели вГрафический интерфейс, но он все еще менее эффективен (ненужные обновления) и выразителен как использование JavaBean*Property s

Как я могу использовать JavaBeanProperties (или аналогичные функции привязки по требованию) без использования java.desktop?

1 Ответ

0 голосов
/ 03 декабря 2018

Вы можете использовать универсальное решение, которое вообще не использует Reflection:

public class DelegatingProperty<B,T> extends ObjectPropertyBase<T>
                                     implements JavaBeanProperty<T> {
    /**
     * Create a property without PropertyChangeEvent based notification.
     */
    public static <O, V> JavaBeanProperty<V> get(O bean, String name,
        Function<? super O, ? extends V> getter,
        BiConsumer<? super O, ? super V> setter) {
        return new DelegatingProperty<>(bean, name, getter, setter, null, null);
    }
    /**
     * Create a property with PropertyChangeEvent based notification.
     */
    public static <O, V> JavaBeanProperty<V> get(O bean, String name,
        Function<? super O, ? extends V> getter, BiConsumer<? super O, ? super V> setter,
        BiConsumer<? super O, ? super PropertyChangeListener> register,
        BiConsumer<? super O, ? super PropertyChangeListener> unregister) {
        return new DelegatingProperty<>(bean, name, getter, setter, register, unregister);
    }
    B bean;
    String name;
    Function<? super B, ? extends T> getter;
    BiConsumer<? super B, ? super T> setter;
    BiConsumer<? super B, ? super PropertyChangeListener> unregister;
    PropertyChangeListener listener;

    private DelegatingProperty(B bean, String name,
        Function<? super B, ? extends T> getter,
        BiConsumer<? super B, ? super T> setter,
        BiConsumer<? super B, ? super PropertyChangeListener> register,
        BiConsumer<? super B, ? super PropertyChangeListener> unregister) {
        this.bean = Objects.requireNonNull(bean);
        this.name = name;
        this.getter = Objects.requireNonNull(getter);
        this.setter = Objects.requireNonNull(setter);
        if(register != null || unregister != null) {
            Objects.requireNonNull(register);
            this.unregister = Objects.requireNonNull(unregister);
            register.accept(bean, listener = event -> fireValueChangedEvent());
        }
    }

    @Override
    public Object getBean() {
        return bean;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public T get() {
        return getter.apply(bean);
    }

    @Override
    public void set(T value) {
        if(isBound()) throw new IllegalStateException("bound property");
        T old = getter.apply(bean);
        setter.accept(bean, value);
        T now = getter.apply(bean);
        if(!Objects.equals(old, now)) fireValueChangedEvent();
    }

    @Override
    protected void invalidated() {
        if(isBound()) {
            setter.accept(bean, super.get());
        }
    }

    @Override
    public void fireValueChangedEvent() {
        super.fireValueChangedEvent();
    }

    @Override
    public void dispose() {
        if(unregister != null && listener != null) {
            unregister.accept(bean, listener);
            listener = null;
        }
    }
}

Тогда, чтобы остаться на вашем примере, вы можете получить свойство name Entity как

JavaBeanProperty<String> prop = DelegatingProperty.get(bean, "name",
    Entity::getName, Entity::setName,
    Entity::addPropertyChangeListener, Entity::removePropertyChangeListener);

Это более многословно, но, с другой стороны, обеспечивает большую безопасность во время компиляции, поскольку наличие всех методов, необходимых для свойства, проверяется во время компиляции и, вероятно, будет иметь более высокую производительность во время выполнения.

Когда у вас есть много свойств в одном классе бинов с поддержкой событий, вы можете воспользоваться специальным фабричным методом, например

public static <V> JavaBeanProperty<V> property(Entity theBean, String name,
    Function<? super Entity, ? extends V> getter,
    BiConsumer<? super Entity, ? super V> setter) {
    return DelegatingProperty.get(theBean, name, getter, setter,
        Entity::addPropertyChangeListener, Entity::removePropertyChangeListener);
}

, который затем вы можете использовать как

JavaBeanProperty<String> nameProp
    = property(bean, "name", Entity::getName, Entity::setName);
JavaBeanProperty<String> otherProp
    = property(bean, "other", Entity::getOther, Entity::setOther);

Конечно, было бы также возможно предоставить их с помощью методов экземпляра самого компонента вместо * фабричного метода static, возможно, с ленивым заполненным полем, содержащим свойство, и т. Д.

Из этой отправной точки можно пройти несколько дорог.

...