美文网首页Kotlin 教程
【kotlin从入门到深坑】之类和继承

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

作者: Android探索之路 | 来源:发表于2017-12-23 22:15 被阅读21次

    简介

    本篇博客主要是介绍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的类和继承相关的知识,多回顾多思考,继续后续内容

    相关文章

      网友评论

        本文标题:【kotlin从入门到深坑】之类和继承

        本文链接:https://www.haomeiwen.com/subject/twfcgxtx.html