On Sat, 10 Jul 2021 12:33:52 GMT, Nir Lisker <nlis...@openjdk.org> 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

Reply via email to