[
https://issues.apache.org/jira/browse/TAP5-2833?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Ben Weidig reassigned TAP5-2833:
--------------------------------
Assignee: Ben Weidig
> SelectModel should adopt upper-bounded wildcards for option collections
> -----------------------------------------------------------------------
>
> Key: TAP5-2833
> URL: https://issues.apache.org/jira/browse/TAP5-2833
> Project: Tapestry 5
> Issue Type: Improvement
> Components: tapestry-core
> Reporter: Ben Weidig
> Assignee: Ben Weidig
> Priority: Minor
>
> h2. Description
> {{SelectModel}} is used to implement domain-specific custom classes that
> implement {{OptionModel}} or {{OptionGroupModel}}.
> However, due to Java's generic type invariance, passing collections of these
> custom implementations (e.g., {{List<MyCustomOptionModel>}}) directly to
> standard {{SelectModel}} constructors, factories, or builders is blocked by
> the compiler.
> This forces developers to write tedious boilerplate code (such as
> stream-mapping or copying arrays) to upcast their collections to
> {{List<OptionModel>}} purely to satisfy the compiler type-checks.
> We can significantly improve developer ergonomics and framework usability by
> modifying input parameter signatures across the selection APIs to adopt
> upper-bounded wildcards (e.g., {{List<? extends OptionModel>}}).
> Furthermore, {{SelectModel.getOptions()}} should become a {{default}} method
> returning {{null}}, as flat selects are by far the most common use case on
> the web.
> h3. Functional Improvement
> * Eliminate redundant collection-copying boilerplate at call sites when
> working with custom implementation lists.
> * Align Tapestry's internal model instantiation APIs with established Java
> Generics design principles ("PECS", Producer Extends, Consumer Super).
> * Simplify {{SelectModel}} implementations by removing repetive {{null}}
> value for groups.
> h2. Technical Tasks / Proposed Solution
> # *Relax Constructor Signatures*: Update concrete constructors (such as
> {{SelectModelImpl}}) to accept wildcards:
> ** Change {{List<OptionGroupModel>}} to {{List<? extends OptionGroupModel>}}
> ** Change {{List<OptionModel>}} to {{List<? extends OptionModel>}}
> # *Relax Utility/Factory Signatures*: Apply the same change to any framework
> helper/factory methods (such as {{SelectModelFactory}}) that consume
> collections to build selection models.
> # *Add default method*: Return {{null}}
> h2. Why the Change?
> * *Framework Ergonomics*: Developers should be able to supply a collection of
> any type implementing {{OptionModel}} directly to a select model without
> manual type conversion. Not needing to implement the same "no groups" method
> repeatedly simplifies the required code.
> * *PECS Best Practice*: Because these constructors and factories only read
> (produce) elements from the provided lists to populate UI components, the
> collections can safely be parameterized with {{? extends}}, which relaxes
> caller constraints without compromising safety.
> h2. Risks / Breaking Changes (Downstream Perspective)
> h3. Method Overrides in Subclasses (Source-Breaking Change)
> *Risk Level*: Low-to-Medium
> If downstream developers have extended concrete classes (such as
> {{SelectModelImpl}} or custom helper components) and overridden setter
> methods, changing those parameter signatures in the parent class will break
> compilation in the subclass.
> In Java, parameter types in overridden methods must match exactly.
> *Mitigation*: Ensure the change is documented in the release notes and
> communicated on the mailing lists so developers can update overridden method
> signatures in their custom subclasses to match the wildcards.
> h3. Modifying Interface Return Types (High Risk - Strongly Discouraged)
> *Risk Level*: High (if return types are changed)
> If the return types of core interface methods (such as
> {{SelectModel.getOptions()}} or {{OptionGroupModel.getOptions()}}) are
> changed from {{List<OptionModel>}} to {{List<? extends OptionModel>}}, it
> will cause immediate compilation errors for any downstream consumer assigning
> the result to a strict {{List<OptionModel>}} variable.
> *Mitigation*: Keep the wildcard relaxation restricted strictly to *input
> parameters* (constructors, setters, and factory inputs). Do not change the
> return types of the getters on public interfaces, preserving backward
> compatibility for existing consumers.
> h3. Binary Compatibility
> *Risk Level*: None
> Due to Java's type erasure, {{List<OptionModel>}} and {{List<? extends
> OptionModel>}} both erase to raw {{java.util.List}} at runtime.
> Existing pre-compiled binaries and compiled third-party modules running
> against this updated version will link and execute successfully without
> throwing {{NoSuchMethodError}}.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)