【问题标题】:Kotlin: why use Abstract classes (vs. interfaces)?Kotlin:为什么使用抽象类(与接口相比)?
【发布时间】:2017-08-10 14:29:47
【问题描述】:

我知道 Kotlin 中抽象类和接口之间的两个区别:

  • 抽象类可以有状态(例如var...)
  • 一个类可以实现多个接口,但不能实现多个抽象类。

既然 Kotlin 是一门比较新鲜的语言,我想知道为什么抽象类没有被抛弃?接口似乎是一种优越的工具,几乎不需要抽象类。

详细说明:Kotlin 确实支持接口中的具体函数实现,例如:

interface Shiny {

    fun shine(amount : Int)  // abstract function

    fun reflect(s : String) { print ("**$s**") }  // concrete function

}

有人可以提供一个需要抽象类的强大实际示例吗?

【问题讨论】:

  • 这实际上与询问“为什么在 java 或 c# 中使用抽象类”或任何其他面向对象的语言相同
  • 我不这么认为:支持接口上的具体功能 - 不是回答这个问题时的状态。湾。 kotlin 很新鲜,可以选择放弃抽象类(与 java 相比 - 必须支持向后兼容性)
  • Kotlin 在编译到 JVM 时没有实现任何高开销的东西。与 Java 6 兼容,它不使用 Java 8 风格的接口默认方法。
  • @LiorBar-On 我愿意 - 这个问题的答案实际上与 Interface vs Abstract Class (general OO) 相同。

标签: kotlin abstract-class


【解决方案1】:

抽象类的实用之处在于,您可以封装与状态一起使用的部分实现,这样它就不会在派生类中被覆盖。

在接口中,您只能定义没有支持字段的属性,并且实现类必须覆盖该属性(使用支持字段或自定义访问器)。

鉴于此,您无法定义以可靠方式将某些状态存储在接口中的逻辑:实现类可能会以意想不到的方式覆盖属性。

例子:

interface MyContainer {
    var size: Int

    fun add(item: MyItem) { 
        // ...
        size = size + 1
    }
}

在这里,我们为add 提供了一个默认实现,它增加了size。但是如果一个实现类是这样定义的,它可能会中断:

class MyContainerImpl : MyContainer {
    override val size: Int 
        get() = 0
        set(value) { println("Just ignoring the $value") }
}

相反,抽象类支持此用例,因此允许您为其所有实现提供一些保证和契约:它们可以定义一些状态及其在派生类中保持不变的转换。

除此之外,抽象类可以有非公共 API(内部的,受保护的)和最终成员,而接口不能(它们只能有私有成员,可以在默认实现中使用),以及它们的所有默认实现可以在类中被覆盖。

【讨论】:

  • “除此之外,抽象类可以有非公共和最终成员,而接口不能” - 似乎接口可以有private 修饰符用于具体功能 - 其余的都是对的:所有接口函数是开放的,不能是内部的。
  • @LiorBar-On,啊,确实,这些函数可以在默认实现中使用,并且对 API 没有贡献。谢谢,更新了答案。
【解决方案2】:

抽象类本质上是为类层次结构而存在的。例如,如果抽象父类有一个具体函数,该函数也在子类中定义,该子类扩展了父类,那么在某些情况下,就需要调用父类的函数。当您使用接口时,由于类的完全抽象性质,不可能这样做。

【讨论】:

  • 我不确定我是否理解:在 Kotlin 接口上支持具体的函数实现。我用一个例子详细说明了我的问题。
  • 引用 Kotlin 网站的一句话“Kotlin 中的接口与 Java 8 非常相似。它们可以包含抽象方法的声明,也可以包含方法实现。它们与抽象类的不同之处在于接口不能存储状态。它们可以具有属性,但这些属性需要是抽象的或提供访问器实现。”除此之外,我相信将接口或抽象类作为对象传递也会有所不同,因为类将具有任何超类,但我不确定 Kotlin 中的接口是否如此。
  • 这不正确:在 Kotlin 中,如果接口提供了默认实现,则实现类可以使用 super.f() 调用该实现,请参阅 this runnable demo
  • @hotkey 这很有趣。为什么 Kotlin 团队决定以这种方式开发语言(您在回答中解释的方式)而不是将其标准化为 java?
  • JetBrains 在 2011 年公开宣布 Kotlin。当时,JSR-355(Java 中的 lambda 表达式和虚拟扩展方法)只是一个早期草案。
【解决方案3】:

其中一个区别是,如果您将其设为接口,我们可以将多个接口继承给一个类,但对于抽象类,只能将单个抽象类继承给一个类(超类型列表中只能出现一个类)

编码愉快!!!

【讨论】:

    猜你喜欢
    • 2015-02-15
    • 2010-11-06
    • 1970-01-01
    • 2013-09-21
    • 2012-08-20
    • 2021-11-10
    • 2012-05-23
    • 2017-02-19
    • 2010-10-12
    相关资源
    最近更新 更多