本章节主要介绍Kotlin类型一些不为人知的秘密


构造器
构造器的基本写法
1 2 3 4
| class Person( var age: Int, name: String )
|
init块
init
块可以有多个
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Person(var age: Int, name: String) { var name: String
init { this.name = name }
val firstName = name.split(" ")[0]
init { } }
|
属性必须被初始化

继承父类
1 2
| abstract class Animal class Person(var age: Int, var name: String) : Animal()
|
副构造器
1 2 3
| class Person(var age: Int, var name: String) : Animal() { constructor(age: Int) : this(age, "unknown") }
|
不定义主构造器(不推荐)

主构造器默认参数
可以为主构造器定义默认参数,使用@JvmOverloads
可以在Java代码中以重载的形式调用

使用同名函数作为工厂函数
1 2 3 4 5
| val persons = HashMap<String, Person>() fun Person(name: String): Person { return persons[name] ?: Person(1, name).also { persons[name] = it } }
|
可见性
类的可见性
可见性类型 |
Java |
Kotlin |
public |
公开 |
与Java相同,默认 |
internal |
❌ |
模块内可见 |
default |
包内可见,默认 |
❌ |
protected |
包内及子类可见 |
类内及子类可见 |
private |
类内可见 |
类或文件内可见 |
修饰对象
可见性类型 |
顶级声明 |
类 |
成员 |
public |
✔️ |
✔️ |
✔️ |
internal |
✔️,模块 |
✔️,模块 |
✔️,模块 |
protected |
❌ |
❌ |
✔️ |
private |
✔️,文件 |
✔️,文件 |
✔️,类 |
模块
直观的讲,大致可以认为是一个Jar包、一个aar
internal VS default
- 一般由
SDK
或公共组件开发者用于隐藏模块内部细节实现
default
可通过外部创建相同包名来访问,访问控制非常弱
default
会导致不同抽象层次的类聚集到相同包之下
internal
可方便处理内外隔离,提升模块内聚减少接口暴露
internal
修饰的kotlin
类或成员在Java
当中可直接访问
类的可见性
1 2
| class Person private constructor(var age: Int, var name: String)
|
属性的可见性
1 2 3 4 5 6 7
| class Person(var age: Int, var name: String) { private var firstName: String = "" var secondName: String = "" private set private get public set }
|
顶级声明的可见性
- 顶级声明指文件内直接定义的属性、函数、类等
- 顶级声明不支持
protected
- 顶级声明被
private
修饰标识文件内部可见
密封类(sealed)
- 密封类是一种特殊的抽象类
- 密封类的子类定义在与自身相同的文件中
- 密封类的子类个数是有限的

密封类的子类
1 2 3 4 5 6 7 8 9 10 11 12
| sealed class PlayerState
object Idle : PlayerState()
class Playing(val song: Song) : PlayerState() { fun start() {} fun stop() {} }
class Error(val errorInfo: ErrorInfo) : PlayerState() { fun recover() {} }
|
子类分支
子类可数,分支完备,所以不需要else分支
1 2 3 4 5 6 7 8 9 10 11 12 13
| this.state = when (val state = this.state) { Idle -> { Playing(song).also(Playing::start) } is Playing -> { state.stop() Playing(song).also(Playing::start) } is Error -> { state.recover() Playing(song).also(Playing::start) } }
|
密封类VS枚举类
|
密封类 |
枚举类 |
状态实现 |
子类继承 |
类实例化 |
状态可数 |
子类可数 |
实例可数 |
状态差异 |
类型差异 |
值差异 |
内联类(inline)
- 内联类是对某一个类型的包装
- 内联类是类似于Java装箱类型的一种类型
- 编译器会尽可能使用被包装的类型进行优化
- 内联类在1.3中处于公测阶段,谨慎使用

内联类可以实现接口,但不能继承父类,也不能被继承
内联类的限制
- 主构造器必须有且只有一个只读属性
- 不能定义有
backing-field
的其他属性
- 被包装类型必须不能是泛型类型
- 不能继承父类也不能被继承
- 内联类不能定义为其他类的内部类
内联类 VS 类型别名
|
typealias |
inline class |
类型 |
没有新类型 |
有包装类型产生 |
实例 |
与原类型一致 |
必要时使用包装类型 |
场景 |
类型更直观 |
优化包装类型性能 |