Community
Participate
Working Groups
Observation: there are two usages of observables. 1. Programmatic usage. The observable is created by client code and its methods are invoked directly. The main benefit of the data binding library in this situation is the ability to use standard content providers, utilities, and transformations. RTTI is never needed for this use case since the client code would always know the type of each observable and its elements. 2. Abstract usage. The client code sets up bindings between things, but does not interact with the observables directly. The client would use observables more like unique identifiers - they would obtain it from a factory and pass it directly to a framework method that selects the correct binding for it. Client code would rarely call methods directly on the observable in this case. These aren't mutually exclusive. The same app will likely use observables in both ways - but I believe that they are the most common use cases. The problem: the RTTI complicates usage 1 even though it is only needed for usage 2. Goal: simplify usage 1 without restricting usage 2 in any way. Suggestion: - Create a new data type called IObservableHandle. It returns an observable and its RTTI: interface IObservableHandle { IObservable getObservable(); Object getValueType(); } - Remove the getElementType() method from IObservableSet and the getValueType() method from IObservableValue. In any situation where this information would be needed, the observable would be wrapped in an IObservableHandle and returned from IObservableHandle.getValueType(). - Change IObservableFactory to return an IObservableHandle rather than an IObservable. - Change any method that makes decisions based on the type of observable (like DataBindingContext.bind (...)) to take an IObservableHandle rather than an IObservable as an argument. Impact: - Any client who is using purely usage 2 would see little difference... but rather than working with IObservables, they'd be working with IObservableHandles. Whenever anyone obtains an observable from a factory, all the RTTI would be available. - Anyone who is implementing an observable or using usage 1 would be able to write simpler code, since their common currency would be the concrete observable interfaces, not IObservableHandle. - It would still be possible to mix-and-match. Someone who obtained an IObservableHandle from a factory and wanted to use it programmatically could do so by downcasting IObservableHandle.getObservable(). Someone who instantiated a concrete observable and wanted to pass it to a method that expects IObservableHandle could easily wrap it in an IObservableHandle. Scripting languages could obtain their observables from the factories, and so would always have the RTTI available for autoboxing.
I forgot to say that this request came from Stefan Xenos.
IMHO, the suggested design is not sufficient. In order for the type information to be preserved, all framework methods would have to accept and return IObservableHandles. Client code that wants to work directly with the IObservable would have to constantly box and unbox, preserving the type information somehow. You also seem to be implying that the IObservableFactories would have the type information. With fully dynamic/polymorphic behavior, no one has this information except the values themselves.
> In order for the type information to be preserved, all framework methods > would have to accept and return IObservableHandles. Yes, most of them would. Clients would only use IObservable directly if they were instantiating their observables and bindings directly, in which case they would not be calling those framework methods. > Client code that wants to work directly with the IObservable would have > to constantly box and unbox, preserving the type information somehow. They wouldn't need to do so often, since most methods would take IObservableHandles, as you observed... and with the use-cases I'm aware of, I rarely need to use the same observable in both ways. Case 1: A binding is obtained through the framework. In which case there is no need to refer to the observable directly. The observable would be boxed when it is obtained and remains boxed. Case 2: A binding is created programmitically. In this case, there is no need to call the framework methods that take handles. The observable is unboxed when it is obtained and remains unboxed. Clients would only need to box/unbox if they are using the same observable in both ways. I have not encountered any situations in my code where this was necessary, but if you have some use-cases where this would occur please describe them. > You also seem to be implying that the IObservableFactories would have the > type information. With fully dynamic/polymorphic behavior, no one has > this information except the values themselves. I'm merely implying that it is functionally equivalent to the current system. Whatever the current semantics of IObservableValue.getValueType() are, and whatever they evolve into, I'm merely suggesting that we move the method to IObservableHandle.getValueType(). It need be no more or less dynamic than it would be if the method were located on IObservableValue.
I spoke with Boris briefly about this over IM last week. I'm not sure when this was logged (old API or static factories?) and if the latest API has had any effect on how Stefan sees his proposed solution. I think we have 4 possibilities: 1) Stefan's proposal. 2) Boris's mentioned an idea of a mixin interface. All factory methods would return an observable that implemented this interface and for non factory created observables we could have a typed and untyped version (e.g. WritableValue - TypedWritableValue) 3) Keep things as they are except make a constructor for the untyped scenarios for nonfactory created observables. The type for the observable would then be Object.class. This would keep from having to introduce other API and also provides the consumer with a programmatic indicator that the type of the value(s) is unchecked. I view this check as a bit cleaner than an instanceof check. 4) Same as 3 except use null as the type. This was suggest by Boris after a brief conversation about #3. The reason for this is because Integer.TYPE.isAssignableFrom(Object.class) == false. A null value would state that no type checking is performed. My +1 goes to #4. Comments? Also, it probably goes unsaid but we'd like to get this worked out and implemented before the 3.3M5 API chill (freeze isn't until M6).
Adding Dave...
ping
+1 for #4 from me. If Dave Orme, Peter Centgraf, and Stefan Xenos remain silent we should go ahead with #4.
OK. I'll reassign this one to myself and I'll get to it this weekend (unless other debate occurs).
Sorry; I'm in the middle of finishing one client and moving to another plus I'm sick. +1 to option #4.
Note to self: When this is implemented we need to reenable the tests in MasterDetailScenario. Currently the SelectionProviderSingleSelectionObservableValue always assumes a type of Object.class. For the implementation of bug 17616 we need to retrieve the PropertyDescriptor of the bean being observed. Because the type is always Object.class this can't be retrieved. So for right now these tests are disabled.
FIXED > 20070128. * Changed any defaulting of the type from Object.class to null for observables. * Changed constructors for Writable*. They each have 4 constructors now. This was done to remove ambiguity. I create a static initializer on all to initialize with a specific type. If we need more we should probably do so with static initializers rather than constructors. * Changed the detail observables so that they don't check the type of the inner observable if the detail type was null. Previously it would attempt to figure out the inner observable type and wouldn't allow it to change. null is being used to convey that no checking is desired. * Reenabled scenaio tests mentioned in comment 10. The only snippet that isn't working as expected is Snippet004DataBindingContextErrorLabel but I don't think that's because of my changes. I'll debug it soon.
VERIFIED in I20070206-0010.