继承类/实现接口
继承类和实现接口都是用的 : ,如果类中没有构造器 ( constructor ),需要在父类类名后面加上 () :
class MainActivity : BaseActivity(), View.OnClickListener
空安全设计
-
不可空类型 val editText : EditText
-
可空类型 val editText : EditText?
lateinit 关键字
-
lateinit 只能修饰 var 可读可写变量
-
lateinit 关键字声明的变量的类型必须是「不可空类型」
-
lateinit 声明的变量不能有「初始值」
-
lateinit 声明的变量不能是「基本数据类型」
-
在构造器中初始化的属性不需要 lateinit 关键字
平台类型
-
@Nullable 表示可空类型
-
@NotNull @NonNull 表示不可空类型
类型判断
-
is 判断属于某类型
-
!is 判断不属于某类型
-
as 类型强转,失败时抛出类型强转失败异常
-
as? 类型强转,但失败时不会抛出异常而是返回 null
获取 Class 对象
-
使用 类名 ::class 获取的是 Kotlin 的类型是 KClass
-
使用 类名 ::class.java 获取的是 Java 的类型
setter/getter
-
在 Kotlin 声明属性的时候 ( 没有使用 private 修饰 ) ,会自动生成一个私有属性和 一对公开的 setter/getter 函数。
-
在写 setter/getter 的时候使用 field 来代替内部的私有属性(防止递归栈溢 出)。
构造器
-
使用 constructor 关键字声明构造器
class User {
constructor()
}
如果我们在构造器主动调用了父类构造,那么在继承类的时候就不能在类的后面加上小括号
constructor(context: Context) : this(context, null)
// 主动调用了父类的构造器
constructor(context: Context, attr: AttributeSet?) : super(context, attr)
class CodeView : TextView {
constructor(context: Context): super(context)
}
等价:
class CodeView(context: Context) : TextView(context) {
}
@JvmField 生成属性
-
通过 @JvmField 注解可以让编译器只生成一个 public 的成员属性,不生成对 应的 setter/getter 函数
Any 和 Unit
-
Any : Kotlin 的顶层父类是 Any ,对应 Java 当中的 Object ,但是比 Object 少了 wait()/notify() 等函数
-
Unit : Kotlin 中的 Unit 对应 Java 中的 void
数组
-
使用 arrayof() 来创建数组,基本数据类型使用对应的 intArrayOf() 等
静态函数和属性
-
顶层函数
-
object
-
companion object
其中,「顶层函数」直接在文件中定义函数和属性,会直接生成静态的,在 Java 中通过「文件名Kt」来 访问,同时可以通过 @file:JvmName 注解来修改这个「类名」。
需要注意,这种顶层函数不要声明在 module 内最顶层的包中,至少要在一个包中例如 com 。不然不能方便使用。
object 和 companion object 都是生成单例对象,然后通过单例对象访问函数和属性的。
@JvmStatic
单例模式/匿名内部类
-
通过 object 关键字实现
// 单例
object Singleton {
}
// 匿名内部类
object : OnClickListener {
}
字符串模版
- 通过 ${} 的形式来作为字符串模版
val number = 100
val text = "向你转账${number}元。"
// 如果只是单一的变量,可以省略掉 {}
val text2 = "向你转账$number元。"
多行字符串
val s = """
我是第一行
我是第⼆行
我是第三行
""".trimIndent()
区间
-
200..299 表示 [200, 299] 的区间 ( 包括 299 )
when 关键字
- Java 当中的 switch 的高级版,分支条件上可以支持表达式
声明接口/抽象类/枚举/注解
// 声明抽象类
abstract class
// 声明接口
interface
// 声明注解
annotation class
// 声明枚举
enum class
编译期常量
-
在静态变量上加上 const 关键字变成编译期常量
标签
-
在 Java 中通过 「 类名 .this 例如 Outer.this 」 获取目标类引用
-
在 Kotlin 中通过「 this@ 类名 例如 this@Outer 」
遍历
记得让 IDE 来帮助生成 for 循环
for(item in items)
内部类
-
在 Kotlin 当中,内部类默认是静态内部类
-
通过 inner 关键字声明为嵌套内部类
可见性修饰符
-
默认的可见性修饰符是 public
-
新增的可见性修饰符 internal 表示当前模块可见
注释
-
注释中可以在任意地方使用 [] 来引用目标,代替 Java 中的 @param @link 等。
非空断言
-
可空类型强制类型转换成不可空类型可以通过在变量后面加上 !! ,来达到类型转换。
open/final
-
Kotlin 中的类和函数,默认是被 final 修饰的 ( abstract 和 override 例外 ) 除非加上 open 关键字才可以被子类覆写(默认方法都是 closed 关闭的,不能被重写的)
次级构造
class CodeView : TextView {
constructor(context: Context): super(context)
}
主构造器
class CodeView constructor(context: Context) : TextView(context)
// 如果没有被「可见性修饰符」「注解」标注,那么 `constructor` 可以省略
class CodeView(context: Context) : TextView(context)
成员变量初始化可以直接访问到主构造参数
class CodeView constructor(context: Context) : TextView(context) {
val color = context.getColor(R.color.white)
}
init 代码块
主构造不能包含任何的代码,初始化代码可以放到 init 代码块中
class CodeView constructor(context: Context) : TextView(context) {
init {
//...
}
}
在初始化的时候,初始化块会按照它们在「文件中出现的顺序」执行。
class CodeView constructor(context: Context) : TextView(context) {
init {
//...
}
val paint = Paint() // 会在 init{} 之后运行
}
构造属性
在主构造参数前面加上 var/val 使构造参数同时成为成员变量
class User constructor(var username: String?, var password: String?, var code: String?)
data class
-
toString()
-
hashCode()
-
equals()
-
copy() (浅拷贝)
-
componentN() ...
相等比较
-
== 结构相等 ( 调用 equals() 比较 )
-
=== 引用 相等 (比较 地址值 )
解构
可以把一个对象「解构」成很多变量
val (code, message, body) = response
对应的 Java 代码
val code = response.component1()
val message = response.component2()
val body = response.component3()
Elvis 操作符
-
可以通过 ?: 的操作来简化 if null 的操作
// lesson.date 为空时使用默认值
val date = lesson.date ?: "日期待定"
// lesson.state 为空时提前返回函数
val state = lesson.state ?: return
// lesson.content 为空时抛出异常
val content = lesson.content ?: throw IllegalArgumentException("content expected")
when 操作符
- when 表达式可以接受返回值,多个分支相同的处理方式可以放在一起,用逗号分隔
val colorRes = when (lesson.state) {
Lesson.State.PLAYBACK, null -> R.color.playback
Lesson.State.LIVE -> R.color.live
Lesson.State.WAIT -> R.color.wait
}
- when 表达式可以用来取代 if-else-if 链。如果不提供参数,所有的分支条件都是布尔表达式
val colorRes = when {
(lesson.state == Lesson.State.PLAYBACK) -> R.color.playback
(lesson.state == null) -> R.color.playback
(lesson.state == Lesson.State.LIVE) -> R.color.live
(lesson.state == Lesson.State.WAIT) -> R.color.wait
else -> R.color.playback
}
operator
- 通过 operator 修饰「特定函数名」的函数,例如 plus 、 get ,可以达到重载运算符的效果
lambda
如果函数的最后一个参数是 lambda ,那么 lambda 表达式可以放在圆括号之外:
lessons.forEach() { lesson : Lesson ->
// ...
}
如果你的函数传入参数只有一个 lambda 的话,那么小括号也可以省略的:
lessons.forEach { lesson : Lesson ->
// ...
}
如果 lambda 表达式只有一个参数,那么可以省略,通过隐式的 it 来访问:
lessons.forEach { // it
// ...
}
循环
通过标准函数 repeat() :
repeat(100) {
//..
}
通过区间:
for (i in 0..99) {
}
// until 不包括右边界
for (i in 0 until 100) {
}
infix 函数
-
必须是 成员函数 或 扩展函数
-
必须 只能接受一个参数,并且不能有默认值
// until() 函数的源码
public infix fun Int.until(to: Int): IntRange {
if (to <= Int.MIN_VALUE) return IntRange.EMPTY
return this .. (to - 1).toInt()
}
潜逃函数
Kotlin 中可以在函数中继续声明函数
fun func(){
fun innerFunc(){
}
}
-
内部函数可以访问外部函数的参数
-
每次调用时,会产生一个函数对象
注解使用处目标
函数简化
可以通过符号 = 简化原本直接 return 的函数
fun get(key :String) = SP.getString(key,null)
函数参数默认值
可以通过函数参数默认值来代替 Java 的函数重载
// 使用 @JvmOverloads 对 Java 暴露重载函数
@JvmOverloads
fun toast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, text, duration).show()
}
扩展函数
-
扩展函数可以为任何类添加上一个函数,从而代替工具类
-
扩展函数和成员函数相同时,成员函数优先被调用
-
扩展函数是静态解析的,在编译时就确定了调用函数 ( 没有多态 )
函数类型
- 函数类型由「传入参数类型」和「返回值类型」组成,用「 -> 」连接,
- 传入参数需要用「 () 」,如果返回值为 Unit 不能省略
- 函数类型实际是一个接口,如果需要将函数作为参数传递时可以通过「 ::函数名 」,或者「匿名函数」或者使用 「 lambda 」
内联函数
- 内联函数配合「函数类型」,可以减少「函数类型」生成的对象
- 使用 inline 关键字声明的函数是「内联函数」,在「编译时」会将「内联函数」中的函数体直接插入到调用处。
- 所以在写内联函数的时候需要注意,尽量将内联函数中的代码行数减少!
部分禁用内联
- noinline 可以禁止部分参数参与内联编译
inline fun foo(inlined: () -> Unit, noinline notInlined:() -> Unit) {
//......
}
具体化的类型参数
- 因为内联函数的存在,我们可以通过配合 inline + reified 达到「真泛型」的效果
val RETROFIT = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build()
inline fun <reified T> create(): T {
return RETROFIT.create(T::class.java)
}
val api = create<API>()
抽象属性
属性委托
-
lazy 延迟属性:值只在第一次访问的时候计算
-
observable 可观察属性:属性发生改变时的通知
-
map 集合:将属性存在一个 map 中
对于一个只读属性(即 val 声明的),委托对象必须提供一个名为 getValue() 的函数
对于一个可变属性(即 var 声明的),委托对象同时提供 setValue() 、 getValue() 函数
类委托
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() {
print(x)
}
}
// Derived 的 print 实现会通过构造参数中的 b 对象来完成。
class Derived(b: Base) : Base by b
Kotlin 标准函数apply、also、run、let
使用时可以通过简单的规则作出一些判断:
-
作用域中使用 this 作为参数 ----> 选择 apply
-
作用域中使用 it 作为参数 ----> 选择 also
-
作用域中使用 this 作为参数 ----> 选择 run
-
作用域中使用 it 作为参数 ----> 选择 let
apply 适合对一个对象做附加操作的时候
let 适合配合判空的时候 (最好是成员变量,而不是局部变量,局部变量更适合用 if )
with 适合对同一个对象进行多次操作的时候