Kotlin继承

时间:2020-02-23 14:37:30  来源:igfitidea点击:

继续我们的Kotlin系列教程,今天,我们将探讨Kotlin类中的继承。
让我们开始创建一个新的IntelliJ IDEA Kotlin项目。

Kotlin继承

继承是创建类层次结构的概念,其中我们根据需要在基类的子类中覆盖基类的属性和功能。

Kotlin中的所有类都有一个共同的超类:Any

继承允许代码可重用。
通常,超类称为基类或者父类,子类为派生/子类。

由于存在著名的钻石问题,Kotlin不允许一个类从多个类继承。

Kotlin继承示例

让我们将上述继承概念放在下面的示例代码中。

我们创建了一个基类,如下所示:

open class Employee(eName: String) {
  init {
      println("He/She is an employee")
  }

  open var name = eName
  var age: Int = 24
  open fun printDetails() {
      println("Employee Name is $name. No specific details are available in this function.")
  }
}

默认情况下,Kotlin中的所有类都是"最终"类。
因此,要允许继承一个类,我们需要在该类之前附加open修饰符,以使其成为非最终类。

另外,要允许覆盖属性和函数,我们也需要在其上设置open修饰符。

一旦创建了类的实例,就会执行init()函数。

以下代码包含派生类,该派生类扩展了上述类。

open class Developer(dName: String, income: Int) : Employee(dName) {
  init {
      println("He/She is an a Developer")
  }

  override var name = dName
  open var salary = income
  
  override fun printDetails() {
      println("Dev name is $name and salary is $salary")
  }
}
  • 要从类继承,我们需要在之后设置基类签名。

  • 如果派生类(开发人员)具有主构造函数,则需要在此使用派生类的参数初始化基类(员工)构造函数。

  • 如果派生类没有主要构造函数,则需要使用关键字" super"在次要构造函数中调用基类构造函数。

  • 要覆盖具有完全相同类型的基类中的属性或者函数,您需要在其后附加override以防止编译错误。

  • 如果在基类中未打开功能和/或者未在基类中设置" override"修饰符,则不能在具有相同签名的派生类中声明功能/属性。

让我们在Kotlin类的主要函数中运行以上代码,以了解程序的流程。

fun main(args: Array<String>) {

  var employee = Employee("Anupam")
  employee.printDetails()

  println()

  var developer = Developer("Hyman", 10000)
  developer.printDetails()
  println(developer.age) //gets the age property from the superclass
  
}

输出应如下所示:

  • 创建开发人员类的实例时,它将首先调用超类的init块,然后调用其自身的块。

  • 子类可以访问其超类的所有非私有属性和函数。

  • 可以将超类实例设置为子类对象。
    这样做,超类只能访问子类中被覆盖的属性和函数。

以下代码展示了将超类实例设置为子类的方法。

employee = developer
employee.printDetails() //Dev name is Hyman and salary is 10000
println(employee.name) //Prints Hyman

从上面的代码中可以明显看出,超类将打印重写的属性和函数。

现在,让我们创建"开发人员"类的子类。

class AndroidDeveloper(name: String, income: Int) : Developer(name, income) {

  override var name = "aName property value is $name".also(::println)
  override var salary: Int = 0
      get() = field
      set(value) {
          if (value >= 100000) {
              field = min(50000, value)
          } else field = value
      }

  init {
      println("He/She is an Android Developer. If Salary >= 100000. It's halved.")
      salary = income
  }

  fun works() {
      println("Builds Apps")
  }

  override fun printDetails() {
      super.printDetails()
      println("He's an Android Developer")

  }
}

class JuniorDeveloper : Developer {
  var aName = "Name is $name".also(::println)

  init {
      println("He/She is a Junior Developer.")
  }

  var mySkill: String?

  override var salary: Int = 0
      get() = field
      set(value) {
          if (value > 50000) {
              field = min(10000, value)
          } else field = value
      }

  constructor(name: String, income: Int, skill: String) : super(name, income) {
      mySkill = skill
      salary = income
  }

  override fun printDetails() {
      println("Junior dev name is $name and salary is $salary, Skill is $mySkill")
  }
}

" JuniorDeveloper"类没有主要的构造函数,因此我们使用" super"在辅助构造函数中调用超类。

在上述两个子类中,我们都在覆盖的属性"工资"中设置了自定义getter和setter。
在定义本身中首次分配属性值时,不会调用setter属性。

让我们再次在main函数中运行类实例。

val androidDeveloper = AndroidDeveloper("Rose", 100000)
  androidDeveloper.printDetails()
  androidDeveloper.salary = 100000
  println("Updated Salary : ${androidDeveloper.salary}")

  println("\nJunior Developer class......\n")
  val juniorDeveloper = JuniorDeveloper("Brock", 60000, "Kotlin")
  juniorDeveloper.printDetails()
  juniorDeveloper.salary = 60000
  println("Junior Dev new salary is ${juniorDeveloper.salary}")
  println("Assigning juniordeveloper to super superclass\n")
  employee = Employee("Anupam")
  employee = juniorDeveloper
  print(employee.salary)

打印以下输出。

因此,对于任何一个实例创建,都将首先调用超类的所有init块。
在JuniorDeveloper类中,我们必须在辅助构造函数中手动设置属性。

因此,对于" juniordeveloper"实例,属性设置器将立即被调用。

在代码的最后一部分,我们将employee实例设置为juniordeveloper实例以获取覆盖的属性。

下一个示例在类层次结构中一起使用接口和类。

open class Manager {
  init {
      println("He/She is a manager.")
  }

  open lateinit var mName: String
  open var salary: Int? = null

  constructor(name: String) {
      mName = name
  }

  constructor(income: Int) {
      salary = income
  }

  open fun printDetails() {
      println("Name is $mName. Salary is $salary")
  }

}

interface X {
  fun printDetails() {
      print("He has an X-Factor")
  }
}

在课堂上,我们创建了两个辅助构造函数。
接口和类具有相同的功能签名。
让我们看看子类如何处理它。

class ProjectManager : Manager {
  var numberOfProjects: Int?
  var pName: String?

  init {
      println("He/She is a Project Manager.")
  }

  constructor(number: Int) : this("Ben", 10)

  constructor(name: String, number: Int) : super(name) {
      numberOfProjects = number
      pName = "Congrats ${super.mName}"
  }

  override fun printDetails() {
      super.printDetails()
      println("$pName He's handled $numberOfProjects projects so far")
  }

}

class BackendManager : Manager, X {

  init {
      println("He/She is a Backend Manager.")
  }

  var isBackendReady: Boolean?
  val backendLanguage: String?

  override var mName = "NA"

  constructor(name: String, language: String) : super(name) {
      println("Constructor 1 gets called.")
      isBackendReady = false
      backendLanguage = language
      mName = name
  }

  constructor(isReady: Boolean, name: String) : this("Anupam", "PHP") {
      println("Constructor 2 gets called.")
      isBackendReady = isReady

  }

  override fun printDetails() {
      super<Manager>.printDetails()
      println("The backend he's working on, is it ready? $isBackendReady.")
      super<X>.printDetails()
  }
}

在class(ProjectManager)中,第一个构造函数使用this委托给第二个构造函数。
因此,第二个构造函数将在第一个构造函数之前执行。

super.mName用于从超类访问属性。

由于它是此类中的"可选",我们需要使用"?。
"运算符安全地将其解包。

由于函数签名在超类和接口中相同,因此要进行正确的调用,我们需要执行" super <Type> .printDetails()"。

创建上述类的实例时的输出如下。

var manager = Manager("Thomas")
  manager.printDetails()

  println()

  manager = ProjectManager(number = 10, name = "Ben")
  manager.printDetails()

  println()

  val backendManager = BackendManager(true, "Smith")
  backendManager.printDetails()

在BackendManager类中,调用构造函数2,该构造函数首先执行构造函数1。