dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.22k stars 1.57k forks source link

@protected should allow use in extensions #56401

Open plammens opened 2 months ago

plammens commented 2 months ago

The @protected annotation should in my opinion allow use in extension methods:

class Class {
  @protected
  void protectedMethod();
}

extension Extension on Class {
  void extensionMethod() => protectedMethod();  // warning: invalid use of protected
}

Dart info

General info

Project info

dart-github-bot commented 2 months ago

Summary: The @protected annotation currently prevents extension methods from accessing protected members of a class, leading to an error message. The user proposes that @protected should allow access from extensions.

lrhn commented 2 months ago

Since @protected doesn't actually protect against access (you can just // ignore the warning if you want to), it may make sense to allow an extension to count as being equivalent to the type itself. The @protected annotation is intended to allow API that should only be used by other members of the same object. Extension members seem to match that, at least as intent.

Just blindly allowing it may leak API that is intended as protected into other libraries without any warning. If the other library is declared an extension method as a helper for themselves, maybe they shouldn't be using protected members for it. (Or maybe they should. It's impossible to say generally.) A compromise could be to allow access to @protected members from extension members that are in the same library as their on type (or its bound, if generic). Then it doesn't matter if a class declares its members as instance members or as extension members (declared in the same library), but if someone else tries to add extension members, they're not considered part of the class they're adding extensions on.

plammens commented 2 months ago

In my use case, I write the extensions in files different from the file where the base class is defined. The protected method is intended to be used in implementing other methods of the subtype, but is not meant to be called directly by external users.

I have some arbitrary data objects, and then a generic class Snapshot<T> which holds a data object of type T and additional database information such as id and location of the object in the database. My data classes are immutable because I make changes directly to the database. For that I need to have access to the database info, so any mutation methods should be in Snapshot<T> and not in T. The problem is that each data class has different things you can modify, so I can't just add everything to the generic Snapshot.

What I do is, whenever I write a data type T, I also write an extension of Snapshot<T> with the relevant mutation methods alongside it. Internally, these methods call an update method that takes a map of field names (strings) to field values. The update method is in the generic Snapshot and marked as protected, because external users shouldn't use it directly, to avoid breaking encapsulation and to profit from type safety: each data class is responsible of how its own data is serialized/deserialized, including field names etc.

Currently, the uses of update inside extensions of Snapshot are marked with a warning.