如何在超级界面上调用覆盖函数?

时间:2018-09-26 12:08:15

标签: kotlin

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具有不保存验证的功能。我决定编写自己的接口,该接口扩展了CrudRepo并覆盖了save方法。在我自己的save定义中,我想验证然后在save上调用CrudRepo方法。

我应该尝试什么?

2 个答案:

答案 0 :(得分:1)

如果您尝试使用super,则会收到一条错误消息,提示“无法直接访问抽象成员”。这适用于接口和类;您不能在super上调用无主体抽象方法。这意味着无法编译:

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

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

但是将CrudRepo界面更改为此:

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

}

确实如此。

如果您想要一个抽象的示例,请转到:

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

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

您将收到相同的错误消息。这是因为您不能调用方法的未实现版本。另外,让我们看一下继承。

interface CustomRepo : CrudRepo
class MyClass : CustomRepo

表示MyClass是CrudRepo和CustomRepo的子代。 MyClass() as CustomRepoMyClass() as CrudRepo都将编译。

现在,这对您的问题意味着什么?

save的角度来看,CrudRepo中没有CustomRepo方法。它尚未实现,因此不能直接调用它。如上所示,调用super.saveAll()意味着call the saveAll method on my parentsuper是一个非常严格的关键字。但是,saveAll()不会被覆盖。您可以 调用save,但是您不能在超级接口上调用它,因为超级接口没有带有正文的保存方法。这句话的最后三个词在这里非常重要。由于CrudBody接口中定义的方法没有主体,因此无法调用它。

此外,由于您已经重写了该方法,因此对该方法的任何调用都将导致递归。如果您这样做:

override fun save(){
    save()
}

它将反复调用自身,直到崩溃。参见What is a StackOverflowError?

现在,如果您有实际班级:

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

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

    override fun validate() {
    }

}

注意如何调用super;现在它将在CustomRepo中调用super方法。但这不是必需的。

当您在save中覆盖CustomRepo时,请记住一件事:您正在覆盖CrudRepo的方法。不再需要任何子类来覆盖它。没有它,我将编译该示例实现。 saveAll有效(没有super关键字)的原因是因为它是抽象的,并且没有引用从中调用它的方法。请参阅StackOverflowError链接。如果调用重写的方法,则将调用子类中的方法,除非在super上调用了该方法。

并且您不能在超类或接口的无体方法上调用super,因为该语言不知道您要指向什么,因为那里什么也没有。

TL; DR:,您不能调用抽象/无体方法的超级方法。 saveAll引用方法的第一个实现,如果在子对象中定义,则引用超类。调用save会导致StackOverflowError。

答案 1 :(得分:0)

我认为Zoes答案包含您需要了解的有关继承或覆盖save方法的所有信息。如果您的CustomRepo最初不是接口,则可以使用delegation,例如:

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

因此,如果您有CrudRepo,只需将其包装到CustomRepo中即可。而已。默认情况下,所有方法都将委派给您的CrudRepo,如果您不希望自己覆盖需要以不同方式工作的函数。

如果您真的想保留自定义interface,那么我可能会做以下事情:

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()
  }
}

因此,如果有人使用ValidatedCrudRepo,则可以确保在实际保存之前调用validate并以同样的方式进行记录。