PropertyUtils { public static <T, U> void bindBidirectionalWithConverter( Property<T> sourceProperty, Property<U> bindingTarget, Function<? super T, ? extends U> t2uConverter, Function<? super U, ? extends T> u2tConverter) { U targetValue = bindingTarget.getValue(); sourceProperty.setValue(targetValue == null ? null : u2tConverter.apply(targetValue)); sourceProperty.addListener((__, old, newValue) -> { U newMappedValue = newValue == null ? null : t2uConverter.apply(newValue); if (EqualityUtils.isDifferent(bindingTarget.getValue(), newMappedValue)) { bindingTarget.setValue(newMappedValue); } }); bindingTarget.addListener((__, old, newValue) -> { T newMappedValue = newValue == null ? null : u2tConverter.apply(newValue); if (EqualityUtils.isDifferent(sourceProperty.getValue(), newMappedValue)) { sourceProperty.setValue(newMappedValue); } }); } private  PropertyUtils(); static void bindBidirectionalWithConverter(
      Property<T> sourceProperty,
      Property<U> bindingTarget,
      Function<? super T, ? extends U> t2uConverter,
      Function<? super U, ? extends T> u2tConverter); static void bindWithConverter(
      Property<T> firstProperty,
      Property<U> secondProperty,
      Function<U, T> u2tConverter); static void bindToMapBidirectionally(Property<T> property,
                                                                  ObservableMap<K, V> map,
                                                                  K key,
                                                                  Function<V, T> v2t); static ObservableList<T> combineLists(ObservableList<T> first, ObservableList<T> second);  }
@Test public void testBindBidirectionalWithConverterNullFirstInitialValue() { Property<String> str = new SimpleStringProperty(null); Property<Number> num = new SimpleDoubleProperty(0.0); PropertyUtils.bindBidirectionalWithConverter(str, num, Double::parseDouble, Object::toString); assertAll( () -> assertEquals("0.0", str.getValue(), "String was not set correctly"), () -> assertEquals(0.0, num.getValue().doubleValue(), "Number should not have changed") ); }
@Test public void testBindBidirectionalWithConverterNullSecondInitialValue() { Property<String> str = new SimpleStringProperty("41"); Property<Number> num = new SimpleObjectProperty<>(null); PropertyUtils.bindBidirectionalWithConverter(str, num, Double::parseDouble, Object::toString); assertAll( () -> assertEquals(null, str.getValue(), "String should not have changed"), () -> assertEquals(null, num.getValue(), "Binding target should not have changed") ); }
@Test public void testBindBidirectionalWithConverter() { Property<String> str = new SimpleStringProperty("-42"); Property<Number> num = new SimpleDoubleProperty(1.23); PropertyUtils.bindBidirectionalWithConverter(str, num, Double::parseDouble, Object::toString); assertAll( () -> assertEquals("1.23", str.getValue(), "String was not set correctly"), () -> assertEquals(1.23, num.getValue().doubleValue(), "Binding target should not have changed") ); str.setValue("89"); assertEquals(89, num.getValue().doubleValue(), "Number was not set correctly"); num.setValue(10.01); assertEquals("10.01", str.getValue(), "String was not set correctly"); }