On Sat, 10 Jul 2021 12:33:52 GMT, Nir Lisker <[email protected]> wrote:
>> Maybe we could simply the mental model of the property specification by
>> making it illegal in all cases to use unidirectional and bidirectional
>> bindings at the same time. The specification would be reduced to "it's
>> illegal", instead of having a long explanation that says it's only _likely_
>> to cause an exception.
>>
>> Most attempts to use both kinds of bindings will fail with an exception
>> anyway. Some will fail instantly when calling `bindBidirectional`, some will
>> fail later when calling `setValue`. Some will fail more silently because the
>> exception will happen inside `BidirectionalBinding` and not bring down the
>> application. Only a kind-of-exotic situation will not produce exceptions at
>> all (the situation that you described in the second bullet point).
>>
>> If we were to always fail instantly at the point of calling `bind` or
>> `bindBidirectional`, we would make the misuse of this API visible where it's
>> most relevant, instead of failing later in other parts of the codebase.
>
> The following unit test demonstrated the *current* behavior, though maybe
> it's not the desired one:
>
>
> import static org.junit.Assert.*;
>
> import javafx.beans.property.IntegerProperty;
> import javafx.beans.property.SimpleIntegerProperty;
>
> import org.junit.Before;
> import org.junit.Test;
>
> public class BindingsTest {
>
> private IntegerProperty property1, property2, property3;
>
> @Before
> public void setup() {
> property1 = new SimpleIntegerProperty(1);
> property2 = new SimpleIntegerProperty(2);
> property3 = new SimpleIntegerProperty(3);
> }
>
> @Test
> public void testBidiAndThenUni() {
> property1.bindBidirectional(property2);
> assertEquals(2, property1.get());
> assertEquals(2, property2.get());
> property1.bind(property3);
> assertEquals(3, property1.get());
> assertEquals(3, property2.get());
> assertEquals(3, property3.get());
> }
>
> @Test(expected = RuntimeException.class)
> public void setProperty1() {
> property1.bindBidirectional(property2);
> property1.bind(property3);
>
> property1.set(4);
> }
>
> @Test
> public void setProperty2() {
> property1.bindBidirectional(property2);
> property1.bind(property3);
>
> // ignores exception (but still prints stack trace) at
> //
> com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)
> property2.set(5);
> assertEquals(3, property1.get());
> assertEquals(3, property2.get()); // "Bidirectional binding
> failed, setting to the previous value"
> assertEquals(3, property3.get());
> }
>
> @Test
> public void setProperty3() {
> property1.bindBidirectional(property2);
> property1.bind(property3);
>
> property3.set(6);
> assertEquals(6, property1.get());
> assertEquals(6, property2.get());
> assertEquals(6, property3.get());
> }
>
> @Test(expected = RuntimeException.class)
> public void testUniAndThenBidiOnBound() {
> property1.bind(property3);
> property1.bindBidirectional(property2);
> }
>
> @Test
> public void testUniAndThenBidiOnUnbound() {
> property1.bind(property3);
> property2.bindBidirectional(property1);
>
> assertEquals(3, property1.get());
> assertEquals(3, property2.get());
> assertEquals(3, property3.get());
> }
> }
>
>
> My observations:
>
> * `setProperty2()` is weird in that it throws an exception internally which
> is ignored by the JVM, and then reverts the change to the unidirectionally
> unbound property when it finds out that its counterpart can't be changed. It
> makes sense logically, but I don't see a valid use case for this.
> * `setProperty3()` behaves as I expect it to, but this is effectively 2
> chained unidirectional binds: `property3` changes `property1` which changes
> `property2`. This is the same as `testUniAndThenBidiOnUnbound()`.
>
> It would make sense to disallow this mixing of bindings on grounds that this
> is probably not what the developer wants to do. If we opt to let them do it
> but write that it should not be done, we could emit a warning. I'm leaning
> towards to fail-fast approach.
I also like documenting it as illegal, but am hesitant to make any
implementation change in this area for JavaFX 17 during rampdown. So I would
recommend documenting as illegal, but not adding the `@throws` to the docs. We
could still say that an exception might be thrown or could say something like
"results are undefined". Then in JavaFX 18, we can add the `@throws` and change
the implementation to fail fast.
-------------
PR: https://git.openjdk.java.net/jfx/pull/533