【kotlin从入门到深坑】之类和继承

来源:互联网 发布:oracle数据库建库 编辑:程序博客网 时间:2024/05/22 04:37

简介

本篇博客主要是介绍Kotlin语法中的【类和继承】相关的知识,帮助各位更快的掌握Kotlin,如果有疏漏或者错误,请在留言中指正,谢谢。 系列汇总地址


声明类

Kotlin中使用关键词class 声明类和java中保持一致,代码如下:

class Demo{ //class + 类名}

类的构成=class +类名+类头(可选)+类体(可选),如果没有类体,括号也可省略

class Demo //class + 类名

主构造函数

在Kotlin的类中可以有一个主构造函数和多个次构造函数。我们看看什么样的算主构造函数,例子如下:

class Demo(){}

看了例子估计心里开始有疑惑了,这和上面的例子有什么不同??仔细一看还真有不同,多了”()”,这就声明了一个无参的主构造函数,当然你也可以声明有参数的,如下

class Demo(name: String){}

主构造函数只是类头的一部分,还有一个可选关键词–constructor,当主构造函数想声明为非public 类型的时候,需要使用该关键词其他时候可以省略,例如:

class Demo private constructor(name: String){}

这种情况一般是在写单例的时候会用到。

构造函数都有了,我们可以愉快的做些初始化了,但是但是要注意,主构造函数内不能包含任何代码。 这个时候我们需要使用另一个关键词 init,使用init 代码块进行初始化代码即可,如下:

class Demo private constructor(name: String) {    init {//init代码块        println("测试输出")    }}

上面的代码中name 可以直接使用在 init代码块内和类体内声明的属性初始化中使用,只有这两种情况,代码如下

class Demo (name: String) {    init {        println("测试输出"+name) //在此处使用可以    }    var nameTest=name //此处给nameTest赋值可以,但不可以单独使用}

当然我们可以让上面的name 有更多的用途怎么办呢?可以这样写:

class Demo(var name: String) { //此处加入 var关键词    init {        println("测试输出" + name)    }    var nameTest = name    fun printTest() { //方法内也可使用了        println(name)    }}

不仅可以使用var关键词,还可以使用val,具体的差别之前我们说过,再次不再赘述。
使用该关键词后,name就等同于是该类的成员变量,就等同于你直接声明在类中。
对于Kotlin来说还有个好用的地方,就是你声明的类中变量不需要写get/set方法,默认就会有,可以直接用。

次构造函数

Kotlin中类也可以声明前缀有 constructor 的次构造函数,如下:

class Demo { //有个默认的无参主构造函数  constructor(){// 有个无参次构造函数  }}

如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数 用 this 关键字即可

下面根据主构造函数是否重写来分别讲解,先说未重写:

class A{}

如何写它的次构造函数呢?

class A {    init {        println("--A主构造--")    }    constructor(name: String) { //此处屏蔽了 默认的主构造函数        println("--A次构造--")    }}

上面的写法会导致Kotlin的默认无参主构造函数不可调用, 也就导致constructor(name: String):this() 失效,但我们上面说过必须要委托给主构造函数,这怎么办?其实Kotlin内部仍然会走默认的主构造函数,也就是说如果是默认主构造是可以省略不写的,有的时候是想写也没法写

得到上面的结论是源于一个测试,代码如下:

var a = A("name"); //此处已经无法调用无参的,也就是默认的主构造失效,内部使用this() 也是不可以的

后面的打印结果

--A主构造--   //仍然走了主构造函数,此时只有默认主构造函数--A次构造--   //然后再走次构造

所以得到上面的结论,在此佐证。 后续用实际代码详解,此处挖坑

有的筒子可能就会想了,如果我们仍然想要一个无参数的构造函数怎么写?

class A {    init {        println("--A主构造--")    }    constructor(name: String) {        println("--A次构造--")    }    constructor(){ //增加一个无参的次构造函数    }}

还有另一个方式,就是重写主构造函数,这两种方法都类似 java中自己重写一个无参的构造函数,现在就是区分了主、次构造函数的重写。还需强调的是主构造如果重写了,次构造不允许再重写。下面写个重写主构造函数的,后面细说:

class A() { //重写此处    init {        println("--A主构造--")    }    constructor(name: String) : this() {        println("--A次构造--")    }//    constructor(){ //次构造不允许使用//        //    }}

下面我们讲一下重写主构造函数的
如下:

class Demo() { //没参数的主构造函数    constructor(name: String) : this() { //次构造函数委托给    }}

此处就和默认的不一样了,这个地方需要使用this(),因为你重写了主构造函数。如果不重写是可以不写this()

通过上方的各种对比和例子我们对类的构造函数有了大概的了解,我们为了方便理解在这里总结一波,还有不明白的多看看例子:

  • 类只有一个主构造函数,但可以有一个或多个次构造函数

  • 次构造函数需要委托给主构造函数,可以通过直接或者间接的方式

  • 在声明期间,以主构造函数为主,如果主构造函数重写了对应的构造方法,次构造函数不能用重复的出现对应的构造函数,上面有例子说明。

  • 在调用期间,以次构造函数为主,如果未重写主构造函数,默认会有一个无参的主构造函数,如果重写一个无参次构造函数,则可调用的只有次构造函数。

创建类的实例

要创建一个类的实例,我们就像普通函数一样调用构造函数:

val invoice = Invoice()val customer = Customer("Joe Smith")//注意 Kotlin 并没有 new 关键字。

继承

在 Kotlin 中所有类都有一个共同的超类 Any,这对于没有超类型声明的类是默认超类:

class Example // 从 Any 隐式继承

Any 不是 java.lang.Object;尤其是,它除了 equals()、hashCode()和toString()外没有任何成员。 更多细节请查阅Java互操作性部分。

要声明一个显式的超类型,我们把类型放到类头的冒号之后:

open class A(p: Int) //注意此处的 open 关键词class B(p: Int) : Base(p) //使用" :"

对于继承的类来说,它仍需满足之前类中主次构造函数的规定,还需满足继承类委托给被继承类的主构造函数(保证对应类的init模块都能运行的关键),此处的被委托的主构造函数如果是无参构造函数,可以省略不写

下面我们分两种类型去分别讲解,首先是未修改默认的主构造函数的情况:
我们先看一下被继承类:

open class BB { //未重写默主构造    constructor(name: String) {        println(name + "---BB")    }    constructor() {        println(".....---BB")    }}

继承类:

class testA : BB { //此处testA也没重写主构造    constructor(name: String) : super(name) {//--    1)        println("A---" + name)    }    constructor() {//--  2)        println("A---" + "ss")    }}

位置1处的构造函数,因为未重写主构造,所以本类的委托完成,所以只需委托父类即可,即可满足上面说的要求。

位置2处的构造函数,因为未重写主构造,所以本类的委托完成,又因为默认是委托父类的无参主构造函数(系统默认的或者重写的都满足),所以满足上面说的要求。

我们做下改动,将B 改动如下:

open class BB() {//重写默认主构造    constructor(name: String):this() { //手动委托主构造        println(name + "---BB")    }//    constructor() { //根据主次函数关系,此处无法存在//        println(".....---BB")////    }}

这时候我们的继承类如何写呢?

class testA : BB {    constructor(name: String) : super(name) {        println("A---" + name)    }    constructor() {        println("A---" + "ss")    }}

可以看到代码是没变化的,也就是说,只要是被继承类的主构造函数,是无参的,无论是默认还是重写都可以省略不写,当然也可以写上

class testA : BB {    constructor(name: String) : super(name) {//可以选择不同的父类的主构造函数进行委托        println("A---" + name)    }    constructor():super() {//无参的委托        println("A---" + "ss")    }}

下面我们将一下修改默认主构造函数的情况

class testB(name: String) : BB(name) { //此处testB重写了默认主构造,所以此处B一定需要用主构造函数(直接或间接的)    init {        println(name + "99ss...---B")    }    constructor(name: String, age: Int) : this(name) {        println(name + "...---B")    }}

也可以这样写:

class testB(name: String) : BB() { //此处必须有"()"    init {        println(name + "99ss...---B")    }    constructor(name: String, age: Int) : this(name) {  //此处不能使用super了,不能重复委托父类,主构造函数已经委托过了        println(name + "...---B")    }}

下面我们总结一波:

  • 如果继承的类重写了默认主构造函数,此时必须用基类的主构造函数初始化(直接或间接)

  • 如果继承的类没重写默认的主构造函数,此时可以使用super关键词初始化,我们也说过,未重写的的默认主构造函数,次构造函数都会委托给它,所以本类内也满足了委托条件,对于类外,也委托了被继承类的主构造方法,完成了两个类的条件


总结

至此已经学完了Kotlin的类和继承相关的知识,多回顾多思考,继续后续内容。