本文收录于 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中特有的单例,后面文章会阐述)
网友评论