美文网首页kotlin入门潜修
kotlin入门潜修之类和对象篇—类和对象

kotlin入门潜修之类和对象篇—类和对象

作者: 寒潇2018 | 来源:发表于2018-11-29 11:09 被阅读0次

    本文收录于 kotlin入门潜修专题系列,欢迎学习交流。

    写在前面

    知人者智,自知者明。胜人者有力,自胜者强。——与君共勉。

    kotlin中定义类的关键字和java一样,都是class,简单示例如下:

    class Hello {}
    

    类的定义主要有三部分组成:name、header以及body(即{}中的内容)。其中header和body都是可省略的,如果一个class没有body,则{}也可以省略。
    类似于下面代码:

    class Hello
    

    构造方法

    众所周知,java中的类都有构造方法,用户如果没有定义则由系统提供一个默认的无参构造方法。kotlin中也有构造方法,但是表达形式却和java相差很大。

    在kotlin中,构造方法被分为了两个类型,一类是主构造方法(primary constructor);另一类被称为第二构造方法(secondary constructor)。其中,主构造方法有且只有一个,而第二构造方法可以有多个。

    主构造方法

    kotlin中的主构造方法示例如下:

    class Person  private constructor(name: String){}
    

    可以看出,主构造方法属于class header,位于class name之后,由constructor关键字来定义,可以有入参。

    当class没有权限修饰符或者注解时(对于这个例子就是没有private)可以省略constructor关键字。如下所示:

    class Person(name: String){}
    

    需要注意的是,主构造方法不能包含任何代码语句,但是如果想要在主构造方法执行的时候做些操作怎么办?kotlin为我们提供了另一套机制来执行主构造方法的初始化。如下所示:

    class Person(name: String, age: Int) {
        val firstProperty = "first property print person name: $name".run(::print)//这里f表示定义了一个属性
        init {//init块
            println("first init block print person name: $name")
        }
        val secondProperty = "second property print person age: $age".run(::print)
        init {
            println("second init block print person age: $age")
        }
    }
    

    代码调用如下:

           @JvmStatic fun main(args: Array<String>) {
                val person = Person("张三", 30)
            }
    

    上面代码执行过后,依次打印如下:

    first property print person name: 张三
    first init block print person name: 张三
    second property print person age: 30
    second init block print person age: 30
    

    这样说明了,init是和属性同级的,谁在前就先执行谁,即按照代码书写顺序执行。同时也表明,主构造方法中的参数可以在class body中初始化属性,如下所示:

    class Person(name: String, age: Int) {
        val upperName = name.toUpperCase()
    }
    

    上面提到了kotlin中的属性,实际上对于用主构造方法初始化属性kotlin提供了更简洁的写法:

    class Person(var name: String, age: Int) {//这里注意name和age定义的区别,name前面加上了var关键字(val关键字也一样),这表示name被定义为了属性
        fun getPersonName():String{
            return this.name//编译正确,因为name被定义为了属性
        }
        fun getPersonAge():Int{
            return this.age//!!!注意,这里无法通过编译,因为age不是属性
        }
    }
    

    再来看一个关于属性的例子:

    class Person(var name: String, age: Int) {
        fun getName():String{//!!!注意,这里是无法编译通过的
            return this.name
        }
    
    }
    

    运行上面的代码发现,根本无法编译通过,为什么?这是因为kotlin中会默认为属性提供get、set方法,故会报方法已定义的错误。

    第二构造方法

    kotlin中可以有多个第二构造方法,其定义关键字依然是constructor,不过是在class body中定义,如下所示:

    class Person {
        constructor(name: String) {//第二构造方法定义
            
        }
    }
    

    注意,当一个类既有主构造方法又有第二构造方法的时候,第二构造方法必须要直接或者间接的委托实现主构造方法(即要首先完成主构造方法的初始化)。如下所示:

    class Person(name: String) {
        constructor(name: String, age: Int) : this(name) {//直接委托实现主构造方法
    
        }
        constructor(name: String, age: Int, sex: String) : this(name, age) {//间接的通过两个参数的第二构造方法委托实现主构造方法
            
        }
    }
    

    从上面代码可以看出,kotlin是通过this关键字来实现委托机制的,从这里来看,this的使用方法和java异曲同工。

    再看下面一个例子:

    class Person(name: String) {
        init {
            println("init execute...")
        }
        constructor(name: String, age: Int) : this(name) {
            println("second execute...")
        }
    }
    

    上述代码执行过后,打印结果如下:

    init execute...
    second constructor execute...
    

    由此可知,在委托机制发生的时候,kotlin会先于第二构造方法执行init块,这是因为init是作为主构造方法的一部分执行的,而主构造方法先于第二构造方法,故init块也会先于第二构造方法执行。

    那如果没有显示定义主构造方法呢?如下所示:

    class Person {//注意这里,并没有定义主构造方法
        init {
            println("init execute...")
        }
        constructor(name: String, age: Int) {
            println("second constructor execute...")
        }
    }
    

    上述代码执行结果为:

    init execute...
    second constructor execute...
    

    从执行结果来看,和有主构造方法执行顺序保持一致。这是为什么?这是因为一个class即使没有主构造方法,委托依然会隐式的发生,所以init会得到执行。

    如果class既没有主构造方法也没有第二构造方法,kotlin会自动生成一个无参的主构造方法,且该方法默认为public,如果想要改变其修饰权限,可以自己定义主构造方法,如下所示:

    class Person {//这里会默认生成一个共有的无参的主构造方法
    }
    
    class Person private constructor(){//通过这种方式可以定义一个私有的主构造方法
    }
    

    此外,如果主构造方法都有默认值(这里的默认值是指所有入参都要有默认值),那么kotlin编译器会为该类再默认生成一个无参的主构造方法,这么做的目的是为了方便第三方库的使用(如jackson使用时需要无参的实例等),如下所示:

            class Person constructor(val name: String = "") {//注意,name有默认值
            }
            //调用处代码
            @JvmStatic fun main(args: Array<String>) {
                val person2 = Person()//注意这里,创建了一个无参的person对象,而Person类并没有提供无参构造方法,证明编译器帮我们生成了一个主无参构造方法
                val person = Person("张三")
            }
    

    对象

    前面介绍kotlin的类相关机制的时候已经用到了其对象,基本和java一致,但是和java有个最大的区别就是,kotlin不在提供new关键字,二者对比如下:

    //java代码
    Person person = new Person()
    
    //kotlin代码
    val person = Person()//注意,这里没有了new关键字
    

    类成员

    前面已经提到了class所包含的部分成员,比如构造方法、属性等,下面罗列下class中具体都可以包含哪些成员:
    — 构造方法和init块
    — 方法(后面文章会分析)
    — 属性(后面文章会分析)
    — 嵌套类和内部类(后面文章会分析)
    — object 定义(即kotlin中特有的单例,后面文章会阐述)

    相关文章

      网友评论

        本文标题:kotlin入门潜修之类和对象篇—类和对象

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