Feature request: It would be nice to have tool to statically describe overwrite policy

When you inherit class you are forced to call its constructor, but when you overwrite its method you can do what ever you want: the only thing class author may use is informal documentation.
I believe that in most cases class may have one of the following policies:

  • Always call super method first (like in constructor)
  • Always call super method as last line (and return its result)
  • Never call super method.

It would be nice to have some annotation like OverwritePolicy(FIRST), OverwritePolicy(LAST) etc.
Compiler may force class inheritor to obey it (refuse to compile if super is not called for example), and for Java we may use inspections.

I belive that by expressing such semantic in formal manner and making compiler/IDE check it statically we may improve code quality

5 Likes

It would be great to see concrete practical use-cases for different overwrite policies. It would be even better if it comes with analysis of alternative solutions to the corresponding use-cases and how they stack against this proposal.

I’v used this approach:

class C1 {
  fun foo() {
    doFoo()
    postprocess()
  }
  fun doFoo() {}
}
class C2 : C1 {
  override fun doFoo() {
  }
}

I think I saw similar approach in Spring Framework. It works but you must mangle name (prepend do for example), so something more elegant would be useful. For example:

class C1 {
  antivirtual fun foo() {
    derived.foo(); // like super.foo() but opposite
    postprocess()
  }
}
class C2 : C1 {
  override antivirtual fun foo() {
  }
}

But I didn’t see anything like that in other languages and I’m not sure if there’s a sane way to map it to JVM, so I don’t know if it’s a good idea.

I’ve seen it in many frameworks where people use inheritance instead of delegation.I am aware of Kotlin delegation abilities, but some frameworks use abstract classes instead of interface or simply force you to use inheritance. Sad, but true.

// Could be form or some kind of configuration
abstract class Configuration {

    /**
     * Serializes object to [destination].
     * Each inheritor should serialize its fields  and call super to serialize parent fields.
     *
     * __Do not forget to call parent before serializatin__
     */
    fun serialize(destination: XmlBuilder) {
    }

    /**
     * Deserializes object from [source]
     * Each inheritor should deserialize its fields  and call super to deserialize parent fields.
     *
     * __Do not forget to call parent after serialization__
     */
    fun deserialize(source: XML) {
    }

    /**
     * Validates configuration and throws [InvalidConfigurationException] in case of error.
     * Each inheritor should validate it self and call super to validate parent.
     *
     * __Do not forget to call parent before validation__
     */
    fun validate() {

    }
}


// From MVC stuff like Struts
abstract class Controller {

    /**
     * If you want to update request before processing it, you may overwrite this method, update reuqest
     * and then call super.
     *
     * __Do not forget to call parent as last step__
     */
    fun showPage(request: Request) {

    }

    /**
     * Default implementation simply returns "true".
     * You can overwrite it and check if user is authorized to access this page,
     * but it does not have any sense to call super here.
     */
    fun isUserAllowedToAccessPage(user: User) = true
}

There is a special inspection for Intellij about similart case, but you can’t configure it because there is no way to provide policy: