Kotlin继承
继续我们的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。