Not long ago, I wrote an article such as "JavaFX can do enough Data Binding!"
Impression of creating a stopwatch app with Java + JavaFX + MVVM
However, ** this is not enough! Not enough! ** Specifically ** I want to write in Generics! ** **
It's a String Property
, a Boolean Property
, ** You didn't think it was a millimeter of shit when you implemented it **, and it was named to ask Java developers (Sun or Oracle) for an hour. I was looking at the class I was in.
"Then let's make a Reactive Property after practicing generics."
For the time being, I decided to inherit the interface Property <T>
while having an instance of type Property <T>
inside. The entity should be a SimpleObjectProperty <T>
that inherits from the Property <T>
, maybe.
sample1.java
public class ReactiveProperty<T> implements Property<T> {
private Property<T> value;
//constructor
public ReactiveProperty(){
value = new SimpleObjectProperty<>();
}
public ReactiveProperty(T t){
value = new SimpleObjectProperty<>(t);
}
}
Since we inherited Property <T>
, we naturally need to implement various methods, but we threw them all into the internal instance (the code is omitted because it is redundant). However, it is unpleasant to have to write a cast when the ʻObject type is an argument method, such as
public void setValue (Object value) {this.value.setValue ((T) value);} `. is not it…….
I'm aiming for an alternative, so I'd like to implement major methods such as Select
and Subscribe
. Regarding the notification method, since the internal instance has a ʻaddListenermethod, I decided to add it there. It's amazing that the notification function works properly with just this. However, Java does not have the concept of "extension method", so the result of
Select suddenly becomes
ReactiveProperty , and
ToReactivePropertyreplaces
StringProperty etc. with ReactiveProperty
sample2.java
public class ReactiveProperty<T> implements Property<T> {
//Create ReactiveProperty from ReactiveProperty
public <R> ReactiveProperty<R> select(Function<T, R> func){
ReactiveProperty<R> retVal = new ReactiveProperty<>(func.apply(this.getValue()));
this.addListener((sb, oldValue, newValue)->{retVal.setValue(func.apply((T)newValue));});
return retVal;
}
//Create a Reactive Property from two Reactive Properties
public <U, R> ReactiveProperty<R> combineLatest(ReactiveProperty<U> u, BiFunction<T, U, R> func){
ReactiveProperty<R> retVal = new ReactiveProperty<>(func.apply(this.getValue(), u.getValue()));
this.addListener((sb, oldValue, newValue)->{retVal.setValue(func.apply((T)newValue, u.getValue()));});
u.addListener((sb, oldValue, newValue)->{retVal.setValue(func.apply(this.getValue(), (U)newValue));});
return retVal;
}
//Create Reactive Property from ◯◯ Property
public static <R> ReactiveProperty<R> ToReactiveProperty(Property<R> r){
ReactiveProperty<R> retVal = new ReactiveProperty<>(r.getValue());
r.addListener((sb, oldValue, newValue)->{retVal.setValue(r.getValue());});
return retVal;
}
//Change notification function
public void subscribe(Consumer<T> cons){
this.addListener((sb, oldValue, newValue) -> {cons.accept(this.getValue());});
}
}
By implementing as above, I was able to create a property with notification by generics. When I brought it to a JavaFX project and used it, it inherits Property <T>
, so it can be Data-bound properly with the property of the control, so it seems to be useful enough.
This is a crappy code written by a Java beginner, but please understand. I'm sorry that the package name is For those who implemented LINQ in Java. Is it necessary to change the model name as well ...?
ReactiveProperty.java
package com.github.jrper;
import javafx.beans.InvalidationListener;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
public class ReactiveProperty<T> implements Property<T> {
private Property<T> value;
//constructor
public ReactiveProperty(){
value = new SimpleObjectProperty<>();
}
public ReactiveProperty(T t){
value = new SimpleObjectProperty<>(t);
}
//Because it inherits the Property interface ...
@Override
public void addListener(ChangeListener listener) {value.addListener(listener);}
@Override
public void removeListener(ChangeListener listener) {value.removeListener(listener);}
@Override
public void setValue(Object value){this.value.setValue((T)value);}
@Override
public void bind(ObservableValue observable) {value.bind(observable);}
@Override
public void unbind() {value.unbind();}
@Override
public boolean isBound() {return value.isBound();}
@Override
public void bindBidirectional(Property other) {value.bindBidirectional(other);}
@Override
public void unbindBidirectional(Property other) {value.unbindBidirectional(other);}
@Override
public Object getBean() {return value.getBean();}
@Override
public String getName() {return value.getName();}
@Override
public void addListener(InvalidationListener listener) {value.addListener(listener);}
@Override
public void removeListener(InvalidationListener listener) {value.removeListener(listener);}
// getter
public T getValue(){return value.getValue();}
//Create ReactiveProperty from ReactiveProperty
public <R> ReactiveProperty<R> select(Function<T, R> func){
ReactiveProperty<R> retVal = new ReactiveProperty<>(func.apply(this.getValue()));
this.addListener((sb, oldValue, newValue)->{retVal.setValue(func.apply((T)newValue));});
return retVal;
}
//Create a Reactive Property from two Reactive Properties
public <U, R> ReactiveProperty<R> combineLatest(ReactiveProperty<U> u, BiFunction<T, U, R> func){
ReactiveProperty<R> retVal = new ReactiveProperty<>(func.apply(this.getValue(), u.getValue()));
this.addListener((sb, oldValue, newValue)->{retVal.setValue(func.apply((T)newValue, u.getValue()));});
u.addListener((sb, oldValue, newValue)->{retVal.setValue(func.apply(this.getValue(), (U)newValue));});
return retVal;
}
//Create Reactive Property from ◯◯ Property
public static <R> ReactiveProperty<R> ToReactiveProperty(Property<R> r){
ReactiveProperty<R> retVal = new ReactiveProperty<>(r.getValue());
r.addListener((sb, oldValue, newValue)->{retVal.setValue(r.getValue());});
return retVal;
}
//Change notification function
public void subscribe(Consumer<T> cons){
this.addListener((sb, oldValue, newValue) -> {cons.accept(this.getValue());});
}
}
Recommended Posts