JavaFX 中的 JavaBeanProperties,无需引入 java.desktop(Swing、AWT)
JavaBeanProperties in JavaFX without pulling in java.desktop (Swing, AWT)
我有一些模型 类 实例化了很多次并且有 很多 字段。虽然我可以将所有字段初始化为 Simple*Property
s,但由于分配,这会显着降低性能(并且延迟创建属性不是一种选择)。
因此,我更愿意使用 JavaBeanProperties
在应用程序中需要的地方创建按需绑定,就像这样(请参阅此答案以获取完整示例 JavaBean wrapping with JavaFX Properties):
Entity bean = ...
StringProperty nameProperty = JavaBeanStringPropertyBuilder()
.bean(bean)
.name("name")
.build();
但是,我不想在 module-info.java
中依赖 java.desktop
和相关的 Swing 组件
我可以通过设置在每个实体的 set* 方法上递增的 SimpleIntegerProperty 来重写所需的功能,然后在 GUI 中添加侦听器,但这仍然效率较低(不必要的更新)和表现力使用 JavaBean*Property
s
如何在不使用 java.desktop 的情况下使用 JavaBeanProperties(或类似的按需绑定功能)?
您可以使用根本不使用反射的通用解决方案:
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;
}
}
}
然后,继续您的示例,您可以获得 Entity
的 name
属性 作为
JavaBeanProperty<String> prop = DelegatingProperty.get(bean, "name",
Entity::getName, Entity::setName,
Entity::addPropertyChangeListener, Entity::removePropertyChangeListener);
它更冗长,但另一方面,提供了更多的编译时安全性,因为 属性 所需的所有方法都在编译时检查,并且可能具有更高的运行时性能.
当你在一个 bean 中有很多属性 class 并支持事件时,你可能会受益于专用的工厂方法,例如
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);
当然,也可以通过 bean 本身的实例方法而不是 static
工厂方法来提供它们,也许还有一个惰性填充的字段保存 属性,等等
从这个起点可以走几条路。
我有一些模型 类 实例化了很多次并且有 很多 字段。虽然我可以将所有字段初始化为 Simple*Property
s,但由于分配,这会显着降低性能(并且延迟创建属性不是一种选择)。
因此,我更愿意使用 JavaBeanProperties
在应用程序中需要的地方创建按需绑定,就像这样(请参阅此答案以获取完整示例 JavaBean wrapping with JavaFX Properties):
Entity bean = ...
StringProperty nameProperty = JavaBeanStringPropertyBuilder()
.bean(bean)
.name("name")
.build();
但是,我不想在 module-info.java
java.desktop
和相关的 Swing 组件
我可以通过设置在每个实体的 set* 方法上递增的 SimpleIntegerProperty 来重写所需的功能,然后在 GUI 中添加侦听器,但这仍然效率较低(不必要的更新)和表现力使用 JavaBean*Property
s
如何在不使用 java.desktop 的情况下使用 JavaBeanProperties(或类似的按需绑定功能)?
您可以使用根本不使用反射的通用解决方案:
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;
}
}
}
然后,继续您的示例,您可以获得 Entity
的 name
属性 作为
JavaBeanProperty<String> prop = DelegatingProperty.get(bean, "name",
Entity::getName, Entity::setName,
Entity::addPropertyChangeListener, Entity::removePropertyChangeListener);
它更冗长,但另一方面,提供了更多的编译时安全性,因为 属性 所需的所有方法都在编译时检查,并且可能具有更高的运行时性能.
当你在一个 bean 中有很多属性 class 并支持事件时,你可能会受益于专用的工厂方法,例如
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);
当然,也可以通过 bean 本身的实例方法而不是 static
工厂方法来提供它们,也许还有一个惰性填充的字段保存 属性,等等
从这个起点可以走几条路。