You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by "Ben Weidig (Jira)" <ji...@apache.org> on 2026/06/21 09:50:00 UTC

[jira] [Assigned] (TAP5-2833) SelectModel should adopt upper-bounded wildcards for option collections

     [ 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)