1.次级构造
class CodeView : TextView {
constructor(context: Context): super(context)
}
2.主构造器
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)
}
3.init 代码块
主构造不能包含任何的代码,初始化代码可以放到 init 代码块中
class CodeView constructor(context: Context) : TextView(context) {
init {
//...
}
}
在初始化的时候,初始化块会按照它们在「⽂件中出现的顺序」执⾏。
class CodeView constructor(context: Context) : TextView(context) {
init {
// ...
}
val paint = Paint() // 会在 init{} 之后运⾏
}
4.构造属性
在主构造参数前⾯加上 var/val 使构造参数同时成为成员变量
class User constructor(var username: String?, var password: String?, var code:
String?)
5.data class
数据类同时会⽣⽣成
- toString()
- hashCode()
- equals()
- copy() (浅拷⻉)
- componentN() ...
6.相等性
- == 结构相等(调用equals()比较)
- === 引用(地址值)相等
7.解构
可以把⼀个对象「解构」成很多变量
val (code, message, body) = response
对应的java代码
val code = response.component1()
val message = response.component2()
val body = response.component3()
8.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")
9.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
}
10.operator
通过 operator 修饰「特定函数名」的函数,例如 plus 、 get ,可以达到重载运算符的效果
11.lambda
如果函数的最后⼀个参数是 lambda ,那么 lambda 表达式可以放在圆括号之外:
lessons.forEach(){ lesson : Lesson ->
// ...
}
如果你的函数传⼊参数只有⼀个 lambda 的话,那么⼩括号可以省略的:
lessons.forEach { lesson : Lesson ->
// ...
}
如果 lambda 表达式只有⼀个参数,那么可以省略,通过隐式的 it 来访问
lessons.forEach { // it
// ...
}
12.循环
通过标准函数 repeat() :
repeat(100) {
//..
}
通过区间:
for (i in 0..99) {
}
// until 不包括右边界
for (i in 0 until 100) {
}
13.infix函数
必须是成员函数或扩展函数
必须只能接受⼀个参数,并且不能有默认值
// until() 函数的源码
public infix fun Int.until(to: Int): IntRange {
if (to <= Int.MIN_VALUE) return IntRange.EMPTY
return this .. (to - 1).toInt()
}
14.嵌套函数
Kotlin 中可以在函数中继续声明函数
fun func(){
fun innerFunc(){
}
}
- 内部函数可以访问外部函数的参数
- 每次调⽤时,会产⽣⼀个函数对象
15.函数简化
可以通过符号=
简化原本直接return
的函数
fun get(key :String) = SP.getString(key,null)
16.函数参数默认值
可以通过函数参数默认值来代替 Java 的函数重载
// 使⽤ @JvmOverloads 对 Java 暴露重载函数
@JvmOverloads
fun toast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, text, duration).show()
}
17.扩展
- 扩展函数可以为任何类添加上⼀个函数,从⽽代替⼯具类
- 扩展函数和成员函数相同时,成员函数优先被调⽤
- 扩展函数是静态解析的,在编译时就确定了调⽤函数(没有多态)
18.函数类型
函数类型由「传⼊参数类型」和「返回值类型」组成,⽤「 -> 」连接,传⼊参数需要⽤「 () 」,如果
返回值为 Unit 不能省略 函数类型实际是⼀个接⼝,我们传递函数的时候可以通过「 ::函数名 」,或者
「匿名函数」或者使⽤ 「 lambda 」
19.内联函数
- 内联函数配合「函数类型」,可以减少「函数类型」⽣成的对象
- 使⽤ inline 关键字声明的函数是「内联函数」,在「编译时」会将「内联函数」中的函数体直接插⼊到调⽤处。
- 所以在写内联函数的时候需要注意,尽量将内联函数中的代码⾏数减少!
20.具体化的类型参数
因为内联函数的存在,我们可以通过配合 inline + reified 达到「真泛型」的效果
val RETROFIT = Retrofit.Builder()
.baseUrl("https://api.hencoder.com/")
.build()
inline fun <reified T> create(): T {
return RETROFIT.create(T::class.java)
}
val api = create<API>()
Reified类型参数仅适用于函数(或具有get()函数的扩展属性),并且仅适用于声明为inline内联的函数。这是一个例子:
inline fun <reified T> Any.isInstanceOf(): Boolean = this is T
当您将函数标记为inline时,编译器会把实现内联函数的字节码插入到每次调用发生的地方。这就是reified类型的工作原理 - 具体实际类型在调用地方是已经知道的,因此在调用x.isInstanceOf<String>()有效地把x编译为String.
reified类型实化参数经常被使用到的几个地方
假设我们有一个User类,以及我们想要读取的JSON字符串
data class User(val first: String, val last: String)
val json = """{
"first": "Sherlock",
"last": "Holmes"
}""
在Java序列化库(如Gson)中,当您想要反序列化该JSON字符串时,您最终必须将Class对象作为参数传递,以便Gson知道您想要的类型。
User user = new Gson().fromJson(getJson(), User.class);
现在,让我们一起展示reified类型实化参数的魔法 我们将创建一个非常轻量级的扩展函数来包装Gson方法:
inline fun <reified T> Gson.fromJson(json: String) =
fromJson(json, T::class.java)
现在,在我们的Kotlin代码中,我们可以反序列化JSON字符串,甚至根本不需要传递类型信息!
val user: User = Gson().fromJson(json)
Kotlin根据它的用法推断出类型 - 因为我们将它分配给User类型的变量,Kotlin使用它作为fromJson()的类型参数。或者,您可以使类型推断其他的类:
val user = Gson().fromJson<User>(json)
在这种情况下,从传递给fromJson()的类型参数推断出user类型。
21.抽象属性
在 Kotlin 中,我们可以声明抽象属性,⼦类对抽象属性重写的时候需要重写对应的 setter/getter
22.属性委托
有些常⻅的属性操作,我们可以通过委托的方式,让它只实现一次,例如:
- lazy延迟属性:值只在第一次访问的时候计算
- observable可观察属性:属性发生改变时的通知
- map集合:将属性存在一个map中
对于一个只读属性(即val声明的),委托对象必须提供一个名为getValue()的函数
对于一个可变属性(即 var 声明的),委托对象同时提供setValue()、getValue()函数
23.类委托
可以通过类委托的模式来减少继承类委托的,编译器会优先使用自身重写的函数,而不是委托对象的函数
网友评论