【问题标题】:How to override method with inherited class in Kotlin?如何在 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 方法。

  1. 我尝试按原样使用它,但不起作用
  2. 我尝试了这个符号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) 无法与我使用的两种指定方法中的任何一种匹配。
  • 啊,是的,我明白了。我将尝试在答案中解释

标签: java kotlin generics inheritance abstraction


【解决方案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 的泛型被破坏了
猜你喜欢
  • 2012-11-20
  • 2021-11-30
  • 2012-09-27
  • 1970-01-01
  • 2012-10-15
  • 2014-01-15
  • 2019-02-08
  • 2016-12-03
  • 1970-01-01
相关资源
最近更新 更多