On Wed, 7 Aug 2024 23:55:39 GMT, Michael Strauß <mstra...@openjdk.org> wrote:

>> Although I'm not advocating for a specific change, I think looking at how `T 
>> convert(Map)` works when it was newly introduced in Java 9 should be taken 
>> into account as well.
>> 
>> There seem to be a couple of general approaches:
>> 
>> 1. Have a marker that can be checked from the outside
>>     - Annotation (checked from the outside)
>>         - Unusual for this kind of check
>>     - Marker interface
>>         - Seen more often, but unusual to not put the new method there
>>     - Subtype 
>>         - Bad idea, you can only inherit once
>> 2. Introduce a new interface with the new method
>>     - Defines the method and serves as the marker at the same time
>> 3. Have a method that can be called that guards a 2nd method call
>>     - Doesn't matter how this is fed (constructor, annotation, override)
>>         -  You see this more often, but it's not a great pattern (2 method 
>> calls for the price of one...)
>> 4. Return a special value from a method that may be unsupported
>>     - The most obvious (modern) candidate is to return `Optional` but is a 
>> bit unusual to indicate support/non-support
>>     - Throw `UnsupportedOperationException` -- although standard, I think it 
>> indicates a programmer mistake that should be avoided (in other words, 
>> encountering this exception in production code should result in a code fix)
>>     - Return `null`
>> 
>> Now the very last option (returning `null`) was the way this was handled 
>> when `T convert(Map)` was introduced in Java 9.  Even though it may not be 
>> the prettiest solution, it has two things going for it:
>> 
>> - It's fast (probably the fastest of the above options, although for most by 
>> just a slim margin)
>> - It fits in with the existing design
>> 
>> So, my order of preference:
>> - Just return `null`
>> - A new interface, which includes the new method
>> - A marker only interface
>> - A supportsXYZ method (regardless of how that is approached)
>> 
>> I don't think I could get behind any of the other options, because they 
>> either stick out like a sore thumb versus the existing design, limit future 
>> options or just perform too poorly.
>
> Returning `null` seems fine from the perspective of `StyleConverter`, but it 
> makes the calling code very awkward. Remember, we ended up here because we 
> needed a way to detect whether an object would support component-wise 
> transitions. If we can't detect that without invoking `convertBack`, we would 
> need to either:
> 1. Speculatively decompose the value without knowing whether there even are 
> any transitions defined on the node. This is bad because most of the time, 
> there will be no transitions; we will end up deconstructing many objects for 
> no reason.
> 2. _Assume_ that an object is component-transitionable, look up all potential 
> transitions, and decompose the value; then, if we were wrong with out 
> assumption, go back to the start and try again with another code path 
> (`Interpolatable` or no transition).
> 
> Instead, what I've implemented now is a new interface 
> `StyleConverter.WithReconstructionSupport`, which contains both methods:
> 
> public interface WithReconstructionSupport<T> {
>     T convert(Map<CssMetaData<? extends Styleable, ?>, Object> values);
>     Map<CssMetaData<? extends Styleable, ?>, Object> convertBack(T value);
> }
> 
> 
> This allows us to use object deconstruction and reconstruction with a single 
> interface reference, and also allows us to detect such support very easily.

+1 for `StyleConverter.WithReconstructionSupport`

maybe use a shorter name, like

`StyleConverter.WithReconstruction`

-------------

PR Review Comment: https://git.openjdk.org/jfx/pull/1522#discussion_r1709803243

Reply via email to