Kotlin 的使用问题
构造函数
我们先来看一段 Java 代码:
1 | public class Test { |
这里可以得到以下结论:
- Java 中可以使用
{}
在类初始化的同时做一些初始化工作 - 不管
{}
的位置如何,它们都先于类的构造函数之前执行
那么,在 Kotlin 中,这段代码该怎么写呢?是下面这样吗:
1 | class Test { |
Kotlin 中这样写可以达成和刚才一样的效果,这里可以得出以下结论:
- Kotlin 中构造函数的方法名统一都是:
constructor
- Kotlin 中如果需要使用
init
前缀的{}
来做一些初始化工作
主构造函数
我们先来看下主构造函数的写法:
1 | class Test constructor(id: Int, name: String) { |
从形式上来看,constructor
构造函数被我们移动到了类名之后,并且类的属性初始化器和 init
代码块中可以使用主构造器中的参数,但是普通函数中无法使用。
这种写法叫做 [主构造函数 primary constructor],一个类最多只能有一个主构造方法,也可以没有。之前的构造函数写法被称为 [次构造函数 secondary constructor],次构造函数可以有多个。主构造函数不能包含任何代码,初始化的代码可以放到 init
前缀的 {}
中。
主构造器有以下两个性质:
- 必须性:当有主构造器存在的时候,次构造器都需要委托给主构造器
- 第一性:类初始化过程中,首先执行的就是主构造器
关于类初始化过程中各模块的执行顺序,可以看下面这个例子:
1 | class Test constructor(id: Int, name: String) { |
初始化过程执行顺序如下:
- 类的主构造函数
- 类的属性初始化器和
init
初始化块按照书写顺序 - 类的次构造函数
初始化过程执行顺序解释:
- 类的属性初始化器和
init
初始化块实际上会成为主构造器的一部分- 当有主构造函数存在的时候,次构造函数需要委托主构造函数。委托给主构造函数会成为次构造函数的第一条语句。
- 当没有主构造函数存在的时候,这种委托还是会隐式发生,并且仍然会执行属性初始化器和
init
初始化块。
默认情况下,主构造函数的 constructor
是可以省略的,上面的代码可以这么写:
1 | class Test(id: Int, name: String) |
以下两种情况,constructor
不可以省略:
- 主构造函数上使用可见性修饰符
- 主构造函数上使用注解
主构造函数中声明属性
刚才主构造函数的参数只能在属性初始化器和 init
初始化块中使用,但是只要给它们加上 val
或者 var
关键字就可以像普通属性一样。也就是说下面的 Kotlin 代码和 Java 代码是等价的。
1 | class Test(var id: Int, var name: String) { |
1 | public class Test { |
当然,除了 val
和 var
关键字,还可以添加可见性修饰符、注解等。
下面代码中是主构造函数在继承过程中可能出现的问题:
1 | open class Test constructor(private var id: Int, private var name: String) { |
1 | abstract class Test constructor(private var id: Int, open var name: String) { |
Object
如果想直接通过类名调用方法,在 Java 中需要使用 static
修饰待访问的属性或者方法。在 Kotlin 中可以使用 object
来完成这个功能。
1 | object Default { |
那么 object
的原理是什么呢?可以反编译成字节码再转换成 Java 代码看看:
1 | public final class Default { |
可以得出结论:
- object 意味着创建一个类,并且创建一个这个类的对象:INSTANCE,也就是单例模式
- object 创建单例的模式是饿汉式,所以是线程安全的
Java 代码访问 object,需要这样访问:
Default.INSTANCE.test()
。可以通过给属性或者方法添加JvmStatic
注解让 Java 代码可以直接访问。
companion object
用 object
修饰的对象中的属性和方法都是静态的,如果只想要一部分属性和方法是静态的可以内部嵌套 object
:
1 | class A { |
类中嵌套的对象可以用 companion
修饰,companion
意味着伴生、伴随,表示修饰的对象和外部类绑定,一个类中最多只可以有一个伴生对象。这样的好处是:
- 外部调用的时候可以省略对象名
companion
修饰时,对象名称可以省略
1 | class A { |
top-level property / function
除了 object
这种,Kotlin 还有更方便的东西:[top-level declaration 顶层声明],就是把属性和方法不写在 class 中:
1 | package com.xl.test |