[英]JavaBeanProperties in JavaFX without pulling in java.desktop (Swing, AWT)
I have some model classes that are instantiated many times and have many fields. 我有一些被多次实例化并具有许多字段的模型类。 While I could initialize all fields as
Simple*Property
s, this significantly slows performance due to the allocations (and lazy-creating the properties is not an option). 虽然我可以将所有字段初始化为
Simple*Property
,但是由于分配的原因,这会大大降低性能(并且不能懒惰地创建属性)。
Thus, I would prefer to use JavaBeanProperties
to create on-demand bindings where needed in the application like so (see this answer for a full example JavaBean wrapping with JavaFX Properties ): 因此,我宁愿使用
JavaBeanProperties
来按需在应用程序中创建按需绑定,如下所示(请参见此答案以获取使用JavaFX Properties包装的完整JavaBean示例):
Entity bean = ...
StringProperty nameProperty = JavaBeanStringPropertyBuilder()
.bean(bean)
.name("name")
.build();
However, I do not want to depend on java.desktop
and related Swing components in my module-info.java
但是,我不想依赖于我的
module-info.java
java.desktop
和相关的Swing组件。
I could potentially re-write the desired functionality by setting a SimpleIntegerProperty that is incremented on each of the Entity's set* methods, and then add listeners in the GUI, but this is still less efficient (unnecessary updates) and expressive as using JavaBean*Property
s 我可以通过设置在每个Entity的set *方法上递增的SimpleIntegerProperty,然后在GUI中添加侦听器,来潜在地重写所需的功能,但这仍然效率较低(不必要的更新),并且表现得与使用
JavaBean*Property
s
How may I use JavaBeanProperties (or similar on-demand binding functionality) without using java.desktop? 在不使用java.desktop的情况下如何使用JavaBeanProperties(或类似的按需绑定功能)?
You could use a generic solution that doesn't use Reflection at all: 您可以使用根本不使用反射的通用解决方案:
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;
}
}
}
Then, to stay at your example, you could get the name
property of Entity
as 然后,以您的示例为例,您可以将
Entity
的name
属性获取为
JavaBeanProperty<String> prop = DelegatingProperty.get(bean, "name",
Entity::getName, Entity::setName,
Entity::addPropertyChangeListener, Entity::removePropertyChangeListener);
It's more verbose, but on the other hand, provides more compile time safety, as the presence of all methods required for the property is checked at compile-time, and will likely have a higher runtime performance. 它较为冗长,但另一方面,它提供了更多的编译时安全性,因为在编译时会检查该属性所需的所有方法的存在,并且可能会具有更高的运行时性能。
When you have a lot of properties in one bean class with event support, you may benefit from a dedicated factory method, eg 当您在一个具有事件支持的bean类中具有许多属性时,您可能会受益于专用的工厂方法,例如
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);
}
which you then can use as 然后您可以将其用作
JavaBeanProperty<String> nameProp
= property(bean, "name", Entity::getName, Entity::setName);
JavaBeanProperty<String> otherProp
= property(bean, "other", Entity::getOther, Entity::setOther);
Of course, it would also be possible to provide them via instance methods of the bean itself instead of a static
factory method, too, perhaps with a lazily populated field holding the property, etc. 当然,也可以通过bean本身的实例方法(而不是
static
工厂方法)来提供它们,也许还可以通过填充属性的懒散字段等来提供。
There are several roads you can go from this starting point. 从这个起点可以走几条路。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.