Ninja420
Ninja420

Reputation: 3872

How to call overriden function on super interface?

interface CrudRepo {
  fun save()
  fun saveAll()
}

interface CustomRepo : CrudRepo {
  fun validate()
  override fun save() {
    validate()
    saveAll() // can call saveAll, I need to call save on CrudRepo here
  }
}

CrudRepo has a function save which doesn't do validation. I decide to write my own interface which extends CrudRepo and overrides the save method. In my own definition of save I want to validate and then call the save method on the CrudRepo.

What should I try ?

Upvotes: 8

Views: 12582

Answers (2)

Roland
Roland

Reputation: 23262

I think Zoes answer contains everything you need to know about inheriting or overriding the save-method. If your CustomRepo wouldn't be an interface in the first place, you could use delegation, e.g.:

class CustomRepo(private val repo : CrudRepo) : CrudRepo by repo {
  fun validate() {}
  override fun save() {
    validate()
    repo.save()
  }
}

So if you have any CrudRepo you just wrap it into your CustomRepo. That's it. All methods will be delegated to your CrudRepo by default and if you do not want that you just override the functions that need to work differently.

If you really want to keep your custom interface then I would probably do something as follows:

interface ValidatedCrudRepo : CrudRepo {
  fun validate()
  /**
   * Ensures that [validate] is called before the actual [save].
   */
  override fun save()
}
class CustomRepo(private val repo : CrudRepo) : ValidatedCrudRepo, CrudRepo by repo {
  override fun validate() {}
  override fun save() {
    validate()
    repo.save()
  }
}

So if anyone uses ValidatedCrudRepo it is ensured that the validate should be called before an actual save and actually also documented that way.

Upvotes: 0

Zoe - Save the data dump
Zoe - Save the data dump

Reputation: 28238

If you try using super, you'll get an error that says "abstract member cannot be accessed directly". This applies to interfaces and classes; you can't call bodyless abstract methods on super. Which means this doesn't compile:

interface CrudRepo {
    fun save()
    fun saveAll()
}

interface CustomRepo : CrudRepo {
    fun validate()
    override fun save() {
        validate()
        super.saveAll() 
    }
}

But change the CrudRepo interface to this:

interface CrudRepo {
    fun save()
    fun saveAll(){
    }

}

And it does.

If you want an abstract example, here you go:

abstract class CrudRepo {
    abstract fun save()
    abstract fun saveAll()
}

class CustomRepo : CrudRepo() {
    override fun saveAll() { }
    override fun save() {
        super.saveAll() 
    }
}

You'll get the same error message. This is because you cannot call unimplemented versions of a method. Also, let's take a look at inheritance.

interface CustomRepo : CrudRepo
class MyClass : CustomRepo

means MyClass is a child of both CrudRepo and CustomRepo. Both MyClass() as CustomRepo and MyClass() as CrudRepo would compile.

Now, what does this mean for your problem?

There is no save method in CrudRepo from the CustomRepo's point of view. It isn't implemented, and as such, you can't call it directly. Calling super.saveAll() as I showed above means call the saveAll method on my parent. super is a very strict keyword. However, saveAll() isn't overridden. You can call save, but you cannot call it on the super interface, because the super interface doesn't have a save method with a body. The last three words of that sentence are incredibly important here; since the method defined in the CrudBody interface doesn't have a body, you can't call it.

And, in addition, since you've overridden the method, any calls to it would result in recursion. If you do this:

override fun save(){
    save()
}

it will call itself over and over, until it crashes. See What is a StackOverflowError?.

Now, if you have an actual class:

class CustomRepoImpl : CustomRepo{
    override fun saveAll() {
    }

    override fun save() {
        super.save()
    }

    override fun validate() {
    }

}

notice how it calls super; it will now call the super method in CustomRepo. It's not required to, however.

And when you override save in CustomRepo, remember one thing: you're overriding a method of CrudRepo. Any child classes are no longer required to override it at all. The example implementation I had would compile without it. The reason saveAll works (without the super keyword) is because it is abstract, and doesn't reference the method in which it is called from. See the StackOverflowError link. If you call an overridden method, the one in the child class is called, unless the method is invoked on super.

And you can't call super on bodyless methods in a superclass or interface, because the language has no idea what you're pointing to, because there's nothing there.

TL;DR: you can't call the super method of an abstract/bodyless method. saveAll refers to the first implementation of the method, or the superclass if defined in a child. Calling save would result in a StackOverflowError.

Upvotes: 6

Related Questions