kotlin学习之类与对象继承与构造函数(代码片段)

唐僧洗澡不秃头 唐僧洗澡不秃头     2022-12-13     607

关键词:

类与对象

首先创建一个Person类。右击包→New→Kotlin File/Class,在弹出的对话框中输入“Person”。对话框在默认情况下自动选中的是创建一个File,File通常是用于编写Kotlin顶层函数和扩展函数的,我们可以点击展开下拉列表进行切换,如图所示。

这里选中Class表示创建一个类,点击“OK”完成创建,会生成如下所示的代码:

class Person 

这是一个空的类实现,可以看到,Kotlin中也是使用class关键字来声明一个类的,这一点和Java一致。现在我们可以在这个类中加入字段和函数来丰富它的功能。

class Person 
    var name = ""
    var age = 0

    fun eat() 
        println(name + " is eating. He is " + age + " years old.")
    

Person类已经定义好了,接下来我们看一下如何对这个类进行实例化,代码如下所示:

val p = Person()

Kotlin中实例化一个类的方式和Java是基本类似的,只是去掉了new关键字而已。

上述代码将实例化后的类赋值到了p这个变量上面,p就可以称为Person类的一个实例,也可以称为一个对象。

下面我们开始在main()函数中对p对象进行一些操作:

fun main() 
    val p = Person()
    p.name = "Jack"
    p.age = 19
    p.eat()

这里将p对象的姓名赋值为Jack,年龄赋值为19,然后调用它的eat()函数,运行结果如图2.18所示。

这就是面向对象编程最基本的用法了,简单概括一下,就是要先将事物封装成具体的类,然后将事物所拥有的属性和能力分别定义成类中的字段和函数,接下来对类进行实例化,再根据具体的编程需求调用类中的字段和方法即可。

继承与构造函数

现在我们开始学习面向对象编程中另一个极其重要的特性——继承。我们先定义一个Student类,在Student类中加入sno和grade字段,同时让Student类去继承Person类,这样Student就自动拥有了Person中的字段和函数,另外还可以定义自己独有的字段和函数。

完成Student类的创建,并在Student类中加入学号和年级这两个字段,代码如下所示:

class Student 
    var sno = ""
    var grade = 0

现在Student和Person这两个类之间是没有任何继承关系的,想要让Student类继承Person类,我们得做两件事才行。

第一件事,使Person类可以被继承。在Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final关键字。

既然现在Person类是无法被继承的,我们得让它可以被继承才行,方法也很简单,在Person类的前面加上open关键字就可以了,如下所示:

open class Person 
    ...

第二件事,要让Student类继承Person类。在Java中继承的关键字是extends,而在Kotlin中变成了一个冒号,写法如下:

class Student : Person() 
    var sno = ""
    var grade = 0

任何一个面向对象的编程语言都会有构造函数的概念,Kotlin中也有,但是Kotlin将构造函数分成了两种:主构造函数和次构造函数。

主构造函数

主构造函数将会是你最常用的构造函数,每个类默认都会有一个不带参数的主构造函数,当然你也可以显式地给它指明参数。主构造函数的特点是没有函数体,直接定义在类名的后面即可。比如下面这种写法:

class Student(val sno: String, val grade: Int) : Person() 

这里我们将学号和年级这两个字段都放到了主构造函数当中,这就表明在对Student类进行实例化的时候,必须传入构造函数中要求的所有参数。比如:

val student = Student("a123", 5)

这样我们就创建了一个Student的对象,同时指定该学生的学号是a123,年级是5。另外,由于构造函数中的参数是在创建实例的时候传入的,不像之前的写法那样还得重新赋值,因此我们可以将参数全部声明成val。

你可能会问,主构造函数没有函数体,如果我想在主构造函数中编写一些逻辑,该怎么办呢?Kotlin给我们提供了一个init结构体,所有主构造函数中的逻辑都可以写在里面:

class Student(val sno1: String, val grade1: Int) : Person() 
    var sno = ""
    var grade = 0
    init 
        sno = sno1
        grade = grade1
        println("sno is " + sno)
        println("grade is " + grade)
    

对于子类中的构造函数必须调用父类中的构造函数,这个规定在Kotlin中也要遵守。Kotlin用了另外一种简单但是可能不太好理解的设计方式:括号。子类的主构造函数调用父类中的哪个构造函数,在继承的时候通过括号来指定。因此再来看一遍这段代码,你应该就能理解了吧。

class Student(val sno: String, val grade: Int) : Person() 

在这里,Person类后面的一对空括号表示Student类的主构造函数在初始化的时候会调用Person类的无参数构造函数,即使在无参数的情况下,这对括号也不能省略。

而如果我们将Person改造一下,将姓名和年龄都放到主构造函数当中,如下所示:

open class Person(val name: String, val age: Int) 
    ...

此时你的Student类一定会报错,当然,如果你的main()函数还保留着之前创建Person实例的代码,那么这里也会报错

现在回到Student类当中,它一定会提示如图2.19所示的错误。

这里出现错误的原因也很明显,Person类后面的空括号表示要去调用Person类中无参的构造函数,但是Person类现在已经没有无参的构造函数了,所以就提示了上述错误。

如果我们想解决这个错误的话,就必须给Person类的构造函数传入name和age字段,可是Student类中也没有这两个字段呀。很简单,没有就加呗。我们可以在Student类的主构造函数中加上name和age这两个参数,再将这两个参数传给Person类的构造函数,代码如下所示:

class Student(val sno: String, val grade: Int, name: String, age: Int) : 
Person(name, age) 
    ...

注意,我们在Student类的主构造函数中增加name和age这两个字段时,不能再将它们声明成val,因为在主构造函数中声明成val或者var的参数将自动成为该类的字段,这就会导致和父类中同名的name和age字段造成冲突。因此,这里的name和age参数前面我们不用加任何关键字,让它的作用域仅限定在主构造函数当中即可。

现在就可以通过如下代码来创建一个Student类的实例:

val student = Student("a123", 5, "Jack", 19)

学到这里,我们就将Kotlin的主构造函数基本掌握了

次构造函数

你要知道,任何一个类只能有一个主构造函数,但是可以有多个次构造函数。次构造函数也可以用于实例化一个类,这一点和主构造函数没有什么不同,只不过它是有函数体的。

Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)。这里我通过一个具体的例子就能简单阐明,代码如下:

class Student(val sno: String, val grade: Int, name: String, age: Int) : 
         Person(name, age)  
    constructor(name: String, age: Int) : this("", 0, name, age) 
    

    constructor() : this("", 0) 
    

次构造函数是通过constructor关键字来定义的,这里我们定义了两个次构造函数:通过this关键字调用了主构造函数,并将sno和grade这两个参数赋值成初始值;第二个次构造函数通过this关键字调用了我们刚才定义的第一个次构造函数,并将name和age参数也赋值成初始值,由于第二个次构造函数间接调用了主构造函数,因此这仍然是合法的。

那么现在我们就拥有了3种方式来对Student类进行实体化,分别是通过不带参数的构造函数、通过带两个参数的构造函数和通过带4个参数的构造函数,对应代码如下所示:

val student1 = Student()
val student2 = Student("Jack", 19)
val student3 = Student("a123", 5, "Jack", 19)

那么接下来我们就再来看一种非常特殊的情况:类中只有次构造函数,没有主构造函数。这种情况真的十分少见,但在Kotlin中是允许的。当一个类没有显式地定义主构造函数且定义了次构造函数时,它就是没有主构造函数的。我们结合代码来看一下:

class Student : Person 
    constructor(name: String, age: Int) : super(name, age) 
    

注意这里的代码变化,首先Student类的后面没有显式地定义主构造函数,同时又因为定义了次构造函数,所以现在Student类是没有主构造函数的。那么既然没有主构造函数,继承Person类的时候也就不需要再加上括号了。

另外,由于没有主构造函数,次构造函数只能直接调用父类的构造函数,上述代码也是将this关键字换成了super关键字。


重写

在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词:

/**用户基类**/
open class Person
    open fun study()       // 允许子类重写
        println("我毕业了")
    


/**子类继承 Person 类**/
class Student : Person() 

    override fun study()    // 重写方法
        println("我在读大学")
    


fun main(args: Array<String>) 
    val s =  Student()
    s.study();


输出结果为:

我在读大学

如果有多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现。

open class A 
    open fun f ()  print("A") 
    fun a()  print("a") 


interface B 
    fun f()  print("B")  //接口的成员变量默认是 open 的
    fun b()  print("b") 


class C() : A() , B
    override fun f() 
        super<A>.f()//调用 A.f()
        super<B>.f()//调用 B.f()
    


fun main(args: Array<String>) 
    val c =  C()
    c.f();


C 继承自 a() 或 b(), C 不仅可以从 A 或则 B 中继承函数,而且 C 可以继承 A()、B() 中共有的函数。此时该函数在中只有一个实现,为了消除歧义,该函数必须调用A()和B()中该函数的实现,并提供自己的实现。

输出结果为:

AB

属性重写

属性重写使用 override 关键字,属性必须具有兼容类型,每一个声明的属性都可以通过初始化程序或者getter方法被重写:

open class Foo() 
    open val x: Int = 0
        get() = field


class Bar1 : Foo() 
    override val x: Int = 0

你可以用一个var属性重写一个val属性,但是反过来不行。因为var属性本身定义了getter,setter方法,var重写为val属性相当于在基类中有一个setter方法,这显然是不合理的

产生以下错误
Val-property cannot override var-property public open var x: Int defined in 5.6.Foo

你可以在主构造函数中使用 override 关键字作为属性声明的一部分:

interface Foo 
    val count: Int


class Bar1(override val count: Int) : Foo

class Bar2 : Foo 
    override var count: Int = 0

本文章参考自郭霖的第一行代码——Android(第3版)

c++基础——c++面向对象之类对象与继承基础总结(类和对象概念构造函数与析构函数this指针继承)(代码片段)

...! 《QT开发实战》《嵌入式通用开发实战》《从0到1学习嵌入式Linux开发》《Android开发实战》《实用硬件方案设计》长期持续带来更多案例与技术文章分享;欢迎商业项目咨询,10年+软硬全栈内功,助力解决您... 查看详情

kotlin类与继承(代码片段)

类kotlin中使用关键字class声明类类声明由类名,类头(指定其类型参数,主构造函数等),花括号包围的类体构成,类头和类体都是可选的。classStudent如果一个类没有类体,则可以省略花括号classEmpty主... 查看详情

android开发学习之路--kotlin之类及对象

前言1类和继承1.1类classMyClass1.2构造函数一个主构造函数以及多个二级构造函数classPersonconstructor(firstName:String)//也可以省略constructor,写成:classPerson(firstName:String)在@Inject的时候不能省略constructorclassPerson@In 查看详情

kotlin类与继承

类Kotlin使用class关键字声明类,类声明由类名、类头(指定其类型参数、主构造函数等)和由大括号包围的类体组成。类头和类体都是可选的,如果一个类没有类体大括号可省略class NoBody类的构造函数一类可以有一个主构造... 查看详情

继承与多态动手动脑(代码片段)

...能反过来?构造函数是一种特殊的方法。主要用来在创建对象时初始化对象,即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数,可根据其参数个数的不同或参数类型的... 查看详情

原型与继承与class(代码片段)

对象有属性(专业点叫静态属性)和方法(专业点叫静态方法)和原型属性和原型方法除了系统自带的那么几百万个对象,我们自己写在js的创建的对象,自定义的对象,都来自对象的构造函数,用来构造对象的函数,叫做构造函... 查看详情

kotlin语法学习(代码片段)

文章目录Kotlin语法学习(二)面向对象编程类和对象继承和构造函数接口访问修饰符数据类和单例Kotlin语法学习(二)面向对象编程类和对象创建一个Person类openclassPerson/***姓名*/varname=""/***年龄*/varage=0/***人的吃饭方法*/fune... 查看详情

学习python之面向对象(代码片段)

学习Python之面向对象python面向对象1.类的定义2.类的实例化3.调用类中的实例方法4.使用实例变量5.类与对象6.类的构造函数7.类变量与实例变量(1).理解类变量与实例变量(2).类变量和实例变量的查找顺序无继承单继承多继承(3).`__... 查看详情

构造函数与原型继承的使用(代码片段)

 functioncreateFn(name)this.name=name;createFn.prototype.study=function()returnthis.name+"在学习";;varcreateFn2=newcreateFn("tom");alert(createFn2.study());//方法调用需要加括号。   查看详情

python基础学习第十五节类与继承(类与继承,这一篇就足够了)(代码片段)

Python基础学习之类与继承1.面向对象名词解释类(Class):用来描述具有相同的属性和方法的对象的集合。类中定义了类对象所共有的属性和方法。对象是类的实例。方法:类中定义的函数,被称为:方法。属性:类中的类变量,我... 查看详情

关于类继承的构造与析构调用分析(代码片段)

  总体结论:派生类的构造函数在创建对象时调用,调用顺序如下:       1.调用虚基类的构造函数(若有多个虚基类,调用顺序为继承的顺序。);       2.调用基类的构造函数(若有多个基类,调用顺序... 查看详情

kotlin学习之路:继承(代码片段)

...承二.继承中的构造函数2.1继承中Java的构造函数2.2继承中Kotlin的构造函数2.3Kotlin多个构造函数的情况三.重写和重载3.1方法的重写3.2属性的重写四.super关键字4.1简单用法4.2复杂情况下的用法4.2.1子类重写方法中使用super4.2.2子类选择... 查看详情

大型程序的工具——多重继承与虚继承(代码片段)

...从每个基类中继承状态  在多重继承关系中,派生类的对象包含有每个基类的子对象。2)派生类构造函数初始化所有基类  构造一个派生类的对象将同时构造并初始化它的所有基类子对象。与从一个基类进行的派生一样,... 查看详情

kotlin学习笔记之面向对象(代码片段)

...ff0c;使用interface表示,示例代码:与java一样,kotlin定义类时要遵循单继承多实现的原则(即只能继承一个父类,可以实现多个接口)kotlin中定义的类和方法默认都是final的,不可重写,如果要实现重... 查看详情

kotlin与java的异同(代码片段)

本文章只为了方便查阅。文章目录1.局部函数和扩展2.定义类继承结构接口open,final和abstract修饰符:默认为finalopenabstract可见性修饰符:默认为public内部类和嵌套类:默认是嵌套类密封类:定义受限的类继承结构3.... 查看详情

kotlin数据类与密封类(代码片段)

/***主构造函数至少包含一个参数*所有的主构造函数的参数必须标识为val或者var*数据类不可以声明为abstractopen、sealed或者inner*数据类不能继承其他类,但是可以实现接口*///关键字为datadataclassUser(valname:String,valage:Int)//funcopy(name:Str... 查看详情

kotlin类与对象②(主构造函数|主构造函数定义临时变量|主构造函数中定义成员属性|次构造函数|构造函数默认参数)(代码片段)

...数四、构造函数默认参数一、主构造函数定义临时变量在Kotlin类中,可以在类声明时在类名后定义"主构造函数";在主构造函数中,可以定义成员属性,并为成员属性提供初始值;在主构造函数中,可以定义临时变量,临时变量一般... 查看详情

javascriptoopjavascript模板与构造函数和继承(代码片段)

查看详情