如何在 Kotlin 中用继承的类覆盖方法?

Posted

技术标签:

【中文标题】如何在 Kotlin 中用继承的类覆盖方法?【英文标题】:How to override method with inherited class in Kotlin? 【发布时间】:2022-01-12 09:16:14 【问题描述】:

我正在探索并积极在 Kotlin 的生产环境中使用泛型。

Kotlin + 泛型对我来说是一个很大的难题,所以也许你可以解释一下并帮助我理解它与 Java 相比是如何工作的。

我有课 AbstracApiClient(不是很抽象)

class AbstracApiClient 
  
  open protected fun makeRequest(requestBuilder: AbstractRequestBuilder) 
    // ... 
  

AbstractRequestBuilder(不是很抽象):

open class AbstractRequestBuilder 
  ...

ConcreteApiClient 继承 AbstractApiClient 应该用从 AbstractRequestBuilder 继承的 ConcreteRequestBuilder 覆盖 makeRequest:

class ConcreteApiClient: AbstractApiClient() 
  
  protected override fun makeRequest(requestBuilder: ConcreteRequestBuilder) 
    // ... 
  


class ConcreteRequestBuilder: AbstractRequestBuilder()

因为我会有更具体的 API 客户端。我想做一个抽象,我可以传递继承的具体请求构建器并覆盖`make requests 方法。

    我尝试按原样使用它,但不起作用 我尝试了这个符号protected open fun <R: ApiRequestBuilder> make request(request builder: R),但它与我想要的覆盖函数不匹配:protected override fun make request(request builder: ConcreteRequestBuilder)

我还有什么其他选择?我在这里遗漏了什么吗?

注意:在这种情况下我不能使用interfaceabstract classes,所以理想情况下我想找到一种继承和函数覆盖的方法。

【问题讨论】:

您能否再解释一下在没有泛型的情况下,目前的方法有什么不适用的?你得到什么错误/使用哪个代码? 覆盖方法在这两种情况下都不起作用,因此方法protected override fun makeRequest(requestBuilder: ConcreteRequestBuilder) 无法与我使用的两种指定方法中的任何一种匹配。 啊,是的,我明白了。我将尝试在答案中解释 【参考方案1】:

你不能用更具体的参数类型覆盖一个方法,因为它会破坏Liskov's substitution principle:

val client: AbstractApiClient = ConcreteApiClient()

client.makeRequest(AbstractRequestBuilder())

正如您在上面看到的,ConreteApiClient 实现必须能够处理父类的所有可能输入,因为它可以通过父类的 API 访问。

要做你想做的事,你需要通过泛型来限制父类本身:

open class AbstractApiClient<R : AbstractRequestBuilder> 
  
  open protected fun makeRequest(requestBuilder: R) 
    // ... 
  


class ConcreteApiClient: AbstractApiClient<ConcreteRequestBuilder>() 
  
  protected override fun makeRequest(requestBuilder: ConcreteRequestBuilder) 
    // ... 
  

这样,AbstractApiClient&lt;R&gt; 的任何实例都必须显示它接受的请求构建器类型(在类型参数中)。它防止了上述问题,因为现在父类型也携带信息:

// doesn't compile
val client: AbstractApiClient<AbstractRequestBuilder> = ConcreteApiClient() 

// this compiles
val client: AbstractApiClient<ConcreteRequestBuilder> = ConcreteApiClient()

我尝试了这种表示法保护的开放乐趣 发出请求(请求生成器:R)

现在关于这个尝试,它不起作用,因为如果你使 method 通用(而不是 class),这意味着方法的每个实现都必须处理 所有种R(不是每个实现一个R)。将泛型放在类上允许为类的每个实例指定一次泛型参数。

【讨论】:

谢谢,我尝试了类似的方法,但我认为中途停止了。我是否正确地认为它在 Java 中是允许的,所以允许 Java 打破 Liskov 的原则? @DmytroChasovskyi Java 也不允许使用更具体的参数类型覆盖方法。如果您尝试使用 @Override 注释该方法,则它不应编译。 然而,Java 在泛型方面确实打破了这些方面的一些事情,因为它允许raw types。因为这个原因,Java 的泛型被破坏了

以上是关于如何在 Kotlin 中用继承的类覆盖方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何继承继承的方法而不是覆盖继承的方法?

派生类中的抽象方法覆盖,如何使私有

Python:装饰一个在继承时打算被覆盖的类方法

Kotlin中级- - - Kotlin类之的继承.md

Kotlin中级- - - Kotlin类之的继承.md

继承和抽象方法