Description
With the new class modifiers feature, we have the ability express classes for which all subtypes are restricted to the same library as the defining class. This provides a path to defining a sound version of field promotion in which final fields
on instances of final
(or sealed
) classes could be subject to promotion. This issue is for discussing the tradeoffs involved.
Reasons not to do this
Several objections have been raised to this proposal, summarized here.
Removing the final
modifier becomes a breaking change
Currently, it is not a breaking change to remove the final
modifier from a class. Allowing promotion on fields of final classes would change this - if the class had any public final
fields, removing final
from the class breaks downstream clients. For sealed
, it is already very breaking to remove the modifier: however since sealed
classes are abstract, they are mostly only useful through their non-sealed
subclasses which just pushes the issue off.
Changing a final field to a getter (and vice versa) becomes a breaking change.
Currently in Dart it is not a breaking change to switch between a final
field and a getter. This field/getter symmetry is valued by many Dart programmers. Allowing promotion of final fields would break this, since changing a final field on a final class would break any clients using promotion. Moving in the other direction (getter to field) is also in principle breaking, since in more limited cases it can be breaking to do promotion where it was not previously done.
Note that this does not just apply to fields defined in final classes. For any public class except one marked interface
, moving between a field and a getter would be breaking:
- For a final class, doing so changes promotion behavior directly
- For a base or simple class, there may be unknown final classes which inherit from the class and expose the member to promotion
Options for addressing the concerns above
Local promotion only
We could allow promotion only for uses of final fields on final classes within the same library as the definition of the class. We'd also need to restrict this to fields which are themselves declared in the same library as the class (rather than inherited) to avoid implicitly promoting a field from another library.
Note that it's not clear that we would want to restrict ourselves to final classes in this case: there's no real reason not to do this for all classes. [Edit: as pointed out below, this doesn't work of course because of overrides]
Explicit opt in at the field declaration
Use some kind of syntax at the definition site of the field (e.g. something like stable getters) to mark fields which are intended to be promoted
cc @dart-lang/language-team @mraleph