⚠️仅适合快速掌握,过一遍知识 .(自用复习)
基础
函数声明
声明函数要用 fun 关键字,就像声明类要用 class 关键字一样
「函数参数」的「参数类型」是在「参数名」的右边函数的
「返回值」在「函数参数」右边使用:分隔,没有返回值时可以省略
声明没有返回值的函数:
fun main() {
//..
}
声明有返回值的参数:
fun sum(x: Int, y: Int): Int {
return x+y
}
变量声明
- var 可读可写变量
- val 只读变量
- 「类型」在「变量量名」的右边,用 : 分割,同时如果满足「类型推断」,类型可以省略创建对象直接调用构造器,不需要new关键字
val name:String = “Kotiln”.
和 Java 的 final 一样,只能声明,赋值一次
继承类 / 实现接口
继承类 / 实现接口 都是使用 : 如何类中没有构造器 constructor ,需要在父类类名后面加上 ():
classMainActivity : BaseActivity(),View.OnClickListener
空安全设计
Kotlin中的类型设计分为 可空类型 和 不可空类型
不可空类型:
val editText : EditText
可空类型:(有可能为 null, 必须声明)
val editText : EditText? //?表示 判断是不是空,不是空才会被调用
调用符
!! 强行调调用符
?. 安全调用符号 //?表示 判断是不是空,非空才会被调用
lateinit 关键字
延迟初始化
lateinit 只能修饰var可读可写变量(思考下为什么) 因为val 只能赋值一次
lateinit 关键字声明的变量的类型必须是「不可空类型」,例如Android的 Edittetx
lateinit 声明的变量不能有「初始值」
lateinit 声明的变量不能是「基本数据类型」,
像 Android的 Edittext可以
在构造器中初始化的属性不需要 lateinit 关键字
类型判断
is 判断属于某类型
!is 判断不属于某类型
as 类型强转,失败时抛出类型强转失败异常
as? 类型强转,但失败时不会抛出异常而是返回null
获取 Class 对象
使用类名 ::class 获取的是 Kotlin 的类型是 KClass
使用类名 ::class.java 获取的是 Java 的类型
eg.
startActivity(Intent(this, LessonActivity::class.java))
setter/getter
默认是这代码,不用写的, Kotlin 在声明的时候已将setter 和 getter 关联
在 Kotlin 声明属性的时候(没有使用private修饰),会自动生成一个私有属性和一对公开的setter/getter函数。
在写setter/getter的时候使用field来代替内部的私有属性(防止递归栈溢出)。
为什么EditText.getText()的时候可以简化,但是 EditText.setText() 的时候不能和TextView.setText()一样简化?
因为EditText.getText()获得的类型是Editable,对应的如果EditText.setText()传入的参数也是Editable就可以简化了。
val newEditable = Editable.Factory.getInstance().newEditable("Kotlin")et_username.text=newEditable
类与对象
可见性修饰符:
包
- 如果你不指定任何可见性修饰符,默认为
public,这意味着你的声明将随处可见; - 如果你声明为
private,它只会在声明它的文件内可见; - 如果你声明为
internal,它会在相同模块内随处可见; protected不适用于顶层声明。- 注意:要使用另一包中可见的顶层声明,仍需将其导入进来。
类和接口
对于类内部声明的成员:
private意味着只在这个类内部(包含其所有成员)可见;protected—— 和private一样 + 在子类中可见。internal—— 能见到类声明的 本模块内 的任何客户端都可见其internal成员;public—— 能见到类声明的任何客户端都可见其public成员。
请注意在 Kotlin 中,外部类不能访问内部类的 private 成员。
如果你覆盖一个 protected 成员并且没有显式指定其可见性,该成员还会是 protected 可见性。
例子:
open class Outer {
private val a = 1
protected open val b = 2
internal val c = 3
val d = 4 // 默认 public
protected class Nested {
public val e: Int = 5
}
}
class Subclass : Outer() {
// a 不可见
// b、c、d 可见
// Nested 和 e 可见
override val b = 5 // “b”为 protected
}
class Unrelated(o: Outer) {
// o.a、o.b 不可见
// o.c 和 o.d 可见(相同模块)
// Outer.Nested 不可见,Nested::e 也不可见
}
构造函数
在一个类中 可以有一个构造函数以及一个或多个次构造函数,主构造函数是类头的一部分,跟在类名(与可选的类型参数)后。
class Person constructor(firstName: String) { /*……*/ }
如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。
**class** Person(firstName: String) { /*……*/ }
如果我们在构造器主动调用了了父类构造,那么在继承类的时候就不能在类的后面加上小括号
constructor(context: Context) : this(context, null)
// 主动调用用了父类的构造器
constructor(context: Context, attr: AttributeSet?) :
super(context, attr)
@JvmField
生成属性通过 @JvmField 注解可以让编译器只生成一个 public 的成员属性,不生成对应的 setter/getter 函数
Any 和 Unit
Any Kotlin 的顶层父类是Any,对应 Java 当中的 Object,但是比 Object 少了wait()/notify() 等函数
Unit Kotlin 中的 Unit 对应 Java 中的 void
数组
arrayOf() 不可变
val arrayOf = arrayOf(1,2,3,4,5) Kotlin 会进行装箱,消耗内存
会使用 专门的数据类型
val intArray = intArrayof(1,2,3,4,6778)
基本数据类型 和包装类型
/****
Java
int long double long
Float null? 包装类型是有可能为null的
Kotlin
Int (不为空的)Float Double Long
(都是对象 都有对应的方法)
Float?
****/
1.toFloat()
kotlin的静态函数定义(三种方法)
(函数和属性都可以定义在文件中)
1.Class 类 中直接定义 (顶层函数 or 包级函数)
调用的时候不需要导包,这种函数叫做 顶层函数 or 包级函数 ,Java 也可以调用eg.
文件名Kt.dp2px(12f);
UtilsKt.dp2px(12f);
2.Object (声明单例对象)
Java中调用:
CacheUtils.INSTANCE.save()
3.companion object 伴生对象
转为Kotlin 👇
compaion obejct : 维护内部类的一个作用 ,是一个单列,不是静态函数
Java 调用: 加上 @JvmStatic 可变为真正的静态
BaseApplication.Companion.currentApplication();
BaseApplication.currentApplication(); (加注解的 @JvmStatic )
Kotlin 为什么会把 static 删掉? 不是更简洁嘛, 但是使用static 声明函数后,这函数就不属于任何对象了 。使用Object 则是加强了这个万物皆对象的概念。
Java 1.8 仅支持 字符串,枚举。
Java 12 的 switch 支持表达式了
其中,「顶层函数」直接在文件中定义函数和属性,会直接生成静态的,在 Java 中通过「文件名Kt」来访问,同时可以通过 @file:JvmName 注解来修改这个「类名」。
需要注意,这种顶层函数不要声明在 module 内最顶层的包中,至少要在一个包中例如 com 。不然不能方便使用。object 和 companion object 都是生成单例例对象,然后通过单例对象访问函数和属性的。
枚举 特殊的class
注解 接口
List 不可修改
ArrayList, MutableList 可修改集合
标签
在 Java 中通过「类名.this例如Outer.this」获取目标类引用在 Kotlin 中通过「this@类名例如this@Outer」获取目标类引用
内部类
Kotlin 中,内部类默认是静态内部类 。没有关键字,
嵌套内部类 有关键字 inner, 可以被外部访问 ,不能有伴生对象
如果想让一个类 在 其他模块中被访问到,但同时又不想让app 模块 访问
Java 不支持,Java 中是 使用 @hide 注解 实现类似功能
Kotlin 模块内访问的修饰符 internal (当前模块可见)
注释
注释中可以在任意地方使用[]来引用目标,代替 Java 中的@param@link等。
非空断言
可空类型强制类型转换成不可空类型可以通过在变量后面加上!!,来达到类型转换。
open / final
Kotlin 中的类和函数,默认是被final修饰的 ( abstract 和 override 例外)
编译期常量
什么是编译期常量:编译过后的字节码,所有引用该常量的地方都会直接替换成值,这样有利于提高代码运行效率。
Java 中 同时被 final 修饰:
private static final String LESSON_PATH = “lesson”
Kotlin 中:
在静态变量上加上const关键字变成编译期常量
const val = "lesson"
进阶
主构造器
成员变了初始化可以直接访问到主构造参数
次级构造
init代码块
主构造不能包含任何的代码,初始化代码可以放到 init 代码块中,自上而下的顺序执行。
在初始化的时候,初始化代码会按照
构造属性
在主构造函数前面加上 val /var 时构造参数同事成为成员变量
data class
数据类同时会生成
toStirng()
hashCode()
equals()
copy() 浅拷贝
componentN() 解构
相等性
== 表示 equal
=== 表示地址比较 有可能是不同的两个对象
解构
可以把一个对象「解构」成很多变量
对应的 Java 代码
Elvis 操作符
可以通过?:的操作来简化if null的操作
?. ?:
eg.
//lesson.date 为空时使用默认值
val date = lesson.date?: "日期待定"
//lesson.state 为空时提前返回
val state = lesson?.state?: return
when 操作符
when 表达式可以接受返回值,多个分支相同的处理方式可以放在一起,用逗号分隔
when 表达式可以用来取代 if-else-if 链。如果不提供参数,所有的分支条件都是布尔表达式
operator
通过 operator 修饰「特定函数名」的函数,例如plus、get,可以达到重载运算符的效果
| 表达式 | 含义 |
|---|---|
| a + b | a.plus(b) |
| a - b | a.minus(b) |
| a * b | a.times(b) |
| a / b | a.div(b) |
lambda
如果函数的最后一个参数是 lambda,那么 lambda 表达式可以放在圆括号之外:
lessons.forEach(){ lesson : Lesson->// ...}
如果你的函数传入参数只有一个 lambda 的话,那么小括号可以省略的:
lessons.forEach { lesson : Lesson->// ...}
如果 lambda 表达式只有一个参数,那么可以省略,通过隐式的it来访问循环 lessons.
lessons.forEach { // it// ...}
循环
1.标准函数repeat():
repeat(100) {
//...
}
2.区间
for (i in 0..99) {
}
infix 函数
必须是成员函数或者拓展函数
必须只能接受一个参数,并且不能有默认值
// until() 函数的源码
public infix fun Int.until(to: Int): IntRange {
if (to<=Int.MIN_VALUE)
return IntRange.EMPTYreturnthis .. (to-1).toInt()
}
嵌套函数
Kotlin 可以在函数中继续声明函数
fun func() {
fun innerFunc(){
}
}
- 内部函数可以访问外部函数的参数
- 每次调用时,会产生一个函数对象
注解使用处目标
函数简化
通过 = 简化 return 的函数
fun get(key :String) = SP.getString(key, null)
函数参数默认值
可以通过函数参数默认值来代替
// 使用 @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.hencoder.com/")
.build()
inline fun<reifiedT>create(): T {
return RETROFIT.create(T::class.java)
}
val api=create<API>()
抽象属性
在 Kotlin 中,可以声明抽象属性, 子类对抽象属性重写的时候需要重写对应的 setter / getter
委托
属性委托
有些常⻅的属性操作,我们可以通过委托的方式,让它只实现一次,例如:
- 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 标准函数
使用时可以通过简单的规则作出一些判断:
-
返回自身 → 和 选
作用域中使用 this 作为参数 →
作用域中使用 it 作为参数 →
-
不需要返回自身 → 从 和 选择
作用域中使用 this 作为参数 →
作用域中使用 it 作为参数 →
apply 适合对一个对象做附加操作的时候,
let 适合配合空判断的时候 (最好是成员变量,而不是局部变量,局部变量更适合用 if )
with 适合对同一个对象进行多次操作的时候