@OverrideShouldInvoke
Kevin Bourrillion
kevinb at google.com
Thu Feb 9 19:58:14 UTC 2023
I think this is a relevant request to the JSpecify <http://jspecify.org>
project; consider filing a feature request here
<https://github.com/jspecify/jspecify/issues/new>. I'm also on the fence
about its relative importance, but it is sometimes asked for (in a much
less well-written-up way than you've already done). It would be fine to
discuss it in that context.
(Tangent re: must vs. should, in JSpecify I haven't found RFC 2119 to be as
directly usable as I'd once hoped, and we're still confounded over how best
to use the terms. It feels more like there is one notion, "must, assuming
that..." with a whole variety of assumptions that might follow, not just a
two-way switch. Also "should" is annoying for having no negation in the
English language, leading to awkward phrases like "is neither encouraged
nor discouraged", while "must" at least sometimes does. Bleh.)
On Thu, Feb 9, 2023 at 8:07 AM Archie Cobbs <archie.cobbs at gmail.com> wrote:
> Just throwing this out there - I'm curious whether this idea resonates
> with anyone else... or alternately offends...
>
> First, a big picture observation:
>
> There is a lot of "activity" in Java centered around the
> superclass-subclass relationship. For example, we have the protected
> keyword. We have the final keyword. We have a bunch of rules around how
> constructors are allowed (or not allowed) to invoke super(). We have
> @Overrides. We have abstract methods that force behavior on subclasses.
>
> Basically, all of this stuff amounts to a somewhat ad hoc "API" that a
> superclass defines and a subclass uses (and in some cases, vice-versa).
>
> But this "API" is not always clearly or fully defined. For example, bugs
> caused by 'this' escapes result from what can be described as an ambiguity
> in the "API" between superclass and subclass.
>
> I guess I'm just saying that this "API" and how we define it (or fail to
> define it) is an area of interest to me.
>
> OK, back to earth...
>
> Here's a proposal that addresses one more little bit of this "API". I'm on
> the fence as to whether this would be worthwhile and am curious what others
> think.
>
> The problem is this: often a superclass method foo() implements some
> important superclass functionality, but it is not final so that it can be
> overridden. In these cases, the superclass often wants to be able to
> specify "If you override this method, then you should also invoke
> super.foo() at some point unless you really know what you're doing". An
> example of such a method is Object.finalize().
>
> There are arguments that such methods are bad style - instead, there
> should be a separate empty method provided for subclasses to override. So
> this idea would have to be weighed against that. But regardless, today
> there are lots of examples of such methods out there already.
>
> The rough proposal is:
>
> - Add a new annotation @OverrideShouldInvoke. If a method Sub.foo()
> overrides a superclass method Sup.foo() that has an
> @OverrideShouldInvoke annotation, and nowhere in Sub.foo() does it
> invoke Sup.foo() (via a super.foo() expression), then an error occurs.
> - Add a new property boolean withoutInvokingSuper default false to the
> @Overrides annotation that allows Sub.foo() to suppress this error if
> it "really knows what it's doing".
>
> For simplicity, we'd only count invocations of the overridden method
> exactly, not some overloaded variant. So for example if an override of
> ArrayList.add(E) invoked super.add(int, E), that wouldn't count. Or, if
> you wanted to get fancy, you could make this optional via something like
> @OverrideShouldInvoke(allowOverloads = true).
>
> Here's are some examples of where you might want to use this:
>
> public class Object {
>
> @OverrideShouldInvoke
> protected native Object clone() throws CloneNotSupportedException;
>
> @OverrideShouldInvoke
> protected void finalize() throws Throwable {
> ...
> }
> }
>
> public class FileOutputStream extends OutputStream {
>
> @OverrideShouldInvoke
> public void close() throws IOException {
> ...
> }
> }
>
> public abstract class AbstractExecutorService implements ExecutorService {
>
> @OverrideShouldInvoke
> public Future<?> submit(Runnable task) {
> ...
> }
> }
>
> Thoughts?
>
> -Archie
>
> --
> Archie L. Cobbs
>
--
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230209/1bdb9bb2/attachment.htm>
More information about the amber-dev
mailing list