<div dir="ltr"><div>I think this is a relevant request to the <a href="http://jspecify.org">JSpecify</a> project; consider filing a feature request <a href="https://github.com/jspecify/jspecify/issues/new">here</a>. 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.</div><div><br></div><div>(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.)</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Feb 9, 2023 at 8:07 AM Archie Cobbs <<a href="mailto:archie.cobbs@gmail.com">archie.cobbs@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>Just throwing this out there - I'm curious whether this idea resonates with anyone else... or alternately offends...</div><div><br></div><div>First, a big picture observation:</div><div><br></div><div>There is a lot of "activity" in Java centered around the superclass-subclass relationship. For example, we have the <span style="font-family:monospace">protected</span> keyword. We have the <span style="font-family:monospace">final</span> keyword. We have a bunch of rules around how constructors are allowed (or not allowed) to invoke <span style="font-family:monospace">super()</span>. We have <span style="font-family:monospace">@Overrides</span>. We have <span style="font-family:monospace">abstract</span> methods that force behavior on subclasses.<br></div><div><br></div><div>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).<br></div><div><br></div><div>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.<br></div><div><br></div><div>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.<br></div><div><br></div><div>OK, back to earth...</div><div><br></div><div>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.<br></div><div><br></div><div>The problem is this: often a superclass method <span style="font-family:monospace">foo()</span> 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 <span style="font-family:monospace">super.foo()</span> at some point unless you really know what you're doing". An example of such a method is <span style="font-family:monospace">Object.finalize()</span>.</div><div><br></div><div>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.<br></div><div><br></div><div>The rough proposal is:</div><div><ul><li>Add a new annotation <span style="font-family:monospace">@OverrideShouldInvoke</span>. If a method <span style="font-family:monospace">Sub.foo()</span> overrides a superclass method <span style="font-family:monospace">Sup.foo()</span> that has an <span style="font-family:monospace">@OverrideShouldInvoke</span> annotation, and nowhere in <span style="font-family:monospace">Sub.foo()</span> does it invoke <span style="font-family:monospace">Sup.foo()</span> (via a <span style="font-family:monospace">super.foo()</span> expression), then an error occurs.<br></li><li>Add a new property <span style="font-family:monospace">boolean withoutInvokingSuper default false</span> to the <span style="font-family:monospace">@Overrides</span> annotation that allows <span style="font-family:monospace">Sub.foo()</span> to suppress this error if it "really knows what it's doing".<br></li></ul></div><div>For simplicity, we'd only count invocations of the overridden method exactly, not some overloaded variant. So for example if an override of <span style="font-family:monospace">ArrayList.add(E)</span> invoked <span style="font-family:monospace">super.add(int, E)</span>, that wouldn't count. Or, if you wanted to get fancy, you could make this optional via something like <span style="font-family:monospace"><a class="gmail_plusreply" id="m_1069550397142745842plusReplyChip-4">@Override</a>ShouldInvoke(allowOverloads = true)</span>.<br></div><div><br></div><div>Here's are some examples of where you might want to use this:</div><div><br></div><div style="margin-left:40px"><span style="font-family:monospace">public class Object {</span></div><div style="margin-left:40px"><span style="font-family:monospace"><br></span></div><div style="margin-left:40px"><span style="font-family:monospace">   <a class="gmail_plusreply" id="m_1069550397142745842plusReplyChip-3">@OverrideShouldInvoke</a></span></div><div style="margin-left:40px"><span style="font-family:monospace">   protected native Object clone() throws CloneNotSupportedException;</span></div><div style="margin-left:40px"><span style="font-family:monospace"><br></span></div><div style="margin-left:40px"><span style="font-family:monospace">  @OverrideShouldInvoke<br>  protected void finalize() throws Throwable {<br>    ...<br>  }</span></div><div style="margin-left:40px"><span style="font-family:monospace">}</span></div><div style="margin-left:40px"><span style="font-family:monospace"><br></span></div><div style="margin-left:40px"><span style="font-family:monospace">public class FileOutputStream extends OutputStream {</span></div><div style="margin-left:40px"><span style="font-family:monospace"><br></span></div><div style="margin-left:40px"><span style="font-family:monospace">   <span style="font-family:monospace"><span style="font-family:monospace"><a class="gmail_plusreply" id="m_1069550397142745842gmail-plusReplyChip-3">@OverrideShouldInvoke</a></span></span></span></div><div style="margin-left:40px"><span style="font-family:monospace">   public void close() throws IOException {</span></div><div style="margin-left:40px"><span style="font-family:monospace">       ...</span></div><div style="margin-left:40px"><span style="font-family:monospace">   }<br></span></div><div style="margin-left:40px"><span style="font-family:monospace">}</span></div><div style="margin-left:40px"><span style="font-family:monospace"><br></span></div><div style="margin-left:40px"><span style="font-family:monospace">public abstract class AbstractExecutorService implements ExecutorService {</span></div><div style="margin-left:40px"><span style="font-family:monospace"><br></span></div><div style="margin-left:40px"><span style="font-family:monospace">   <span style="font-family:monospace"><a class="gmail_plusreply" id="m_1069550397142745842gmail-plusReplyChip-3">@OverrideShouldInvoke</a></span></span></div><div style="margin-left:40px"><span style="font-family:monospace">   public Future<?> submit(Runnable task) {</span></div><div style="margin-left:40px"><span style="font-family:monospace">       ...</span></div><div style="margin-left:40px"><span style="font-family:monospace">   }<br></span></div><div style="margin-left:40px"><span style="font-family:monospace">}<br></span></div><div><br></div><div>Thoughts?<br></div><div><br></div><div>-Archie</div><div><br></div><div>--<br></div><div><div dir="ltr">Archie L. Cobbs<br></div></div></div>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div><div dir="ltr"><div style="line-height:1.5em;padding-top:10px;margin-top:10px;color:rgb(85,85,85);font-family:sans-serif"><span style="border-width:2px 0px 0px;border-style:solid;border-color:rgb(213,15,37);padding-top:2px;margin-top:2px">Kevin Bourrillion |</span><span style="border-width:2px 0px 0px;border-style:solid;border-color:rgb(51,105,232);padding-top:2px;margin-top:2px"> Java Librarian |</span><span style="border-width:2px 0px 0px;border-style:solid;border-color:rgb(0,153,57);padding-top:2px;margin-top:2px"> Google, Inc. |</span><span style="border-width:2px 0px 0px;border-style:solid;border-color:rgb(238,178,17);padding-top:2px;margin-top:2px"> <a href="mailto:kevinb@google.com" target="_blank">kevinb@google.com</a></span></div></div></div></div></div></div></div>