kotlin的笔记

Android Studio创建kotlin项目

File->New->New Project->Include kotlin support

project build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter(){url 'https://maven.aliyun.com/repository/jcenter/'}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter(){url 'https://maven.aliyun.com/repository/jcenter/'}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

app build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
......
......
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.core:core-ktx:1.2.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.6"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.6"
..........
........
}

修饰符

类修饰符

Java的类和方法默认是open的,而kotlin中默认都是final的。如果创建一个类的子类,需要使用open修饰符来标示这个类,此外还需要给每一个可以被重写的属性或方法添加open修饰符。

修饰符 说明
final 不能被继承
open 可以被继承
abstract 抽象类
enum 枚举类
data 数据类
sealed 密封类
annotation 注解类

成员修饰符
|修饰符| 说明|
|-|-|
|override| 重写函数|
|open| 可被重写|
|final| 不能被重写|
|abstract| 抽象函数|
|iateinit| 后期初始化|

访问修饰符

修饰符 说明
private 私有
protected 子类可见
internal 在同一个模块(module)内可以访问
public 任何地方可见

数据类型

定义变量关键字

定义变量关键字 说明
var 可变变量
val 只读变量 创建的时必须初始化

Any类型是 Kotlin 中 所有非空类型(ex: String, Int) 的根类型

1
2
3
4
5
6
7
8
9
//默认是不能赋值为空的
var a:String = null
//这样是允许为空的
var a:String? = null
//使用安全操作符 类型转换也可以用
println(a?.length) //不报错 输出null
bob?.department?.head?.name //不报错 只要有一个null就输出null
println(a?.length ?: -2)//不报错 表示null时候返回-2
//val len = b ! ! . length 为空会抛出空指针

基本数据类型

基本数据类型
Int
Long
Float
Double
Boolean
Char
String

例子 定义变量

1
2
3
var i:Int = 0
var j:Int
j=9

控制语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//kotlin没有三元运算符用if else 代替 如
var type:Int=1
var result:String=if (type==1) "类型1" else "类型2"
//switch/case 用 when/else 代替 自带break
var result:String="1"
when(result){
"1"->"类型1"
"2","3"->"类型2"
else-> "类型3"
}
//如果是数字还能这样玩
var num:Int=3
when(num){
2,5,8->"类型1"
in 1..3->"类型2"//1-3
!in 1..3->"类型3"
else-> "类型4"
}

基本数据类型转换

类型转换
toInt
toLong
toFloat
toDouble
toChar
toString
toCharArray

例子 变量转换

1
2
3
4
5
6
var i:Int = 1
var str:String=i.toString()
var p3=a==c
var p4=a===c//内存地址也要相等

数组类型

数组类型 初始化方法 例子
IntArray intArrayOf var i:IntArray=intArrayOf{1,2,3} 或者 var i:Array< Int>=arrayOf(1,2,3)
LongArray longArrayOf 参考IntArray
FloatArray floatArrayOf 参考IntArray
DoubleArray doubleArrayOf 参考IntArray
CharArray charArrayOf 参考IntArray
BooleanArray booleanArrayOf 参考IntArray
StringArray 只能 var i:Array< String>=arrayOf(“2”,”2”,”2”)

数组循环操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var i:Array< String>=arrayOf("2","2","2")
var size:Int= i.size()
//访问数组的两种方式
i[0]
i.get(0)
val s:Array<String> = arrayOf("ha","yo","qq")
for (item in s){
Log.e("遍历1",item)
}
for (i in s.indices){
Log.e("遍历2",s[i])
}
for (i in 0 until 3){//0 1 2
Log.e("遍历3",s[i])
}
for (i in 0..2 step 1){//从0开始到2 每次加1
Log.e("遍历4",s[i])
}
i.forEach{
print("$it ") //这里的it指代被遍历的list中的值
}

函数运用

1
2
3
4
5
6
7
8
9
10
//在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法 方便java调用
@JvmOverloads fun change(a:String,b:String="默认参数可以不传",c:String):Int{
return a.toInt()
}
change(a,c="可以指定字段传")
fun change1(vararg d:String?){//可变参数
}
fun change3(c:Int):Int=if (c>1) c else 3*change3(3)//递归

类和对象

get set

1
2
3
4
5
6
7
8
9
10
11
class Animal1 {
//get set 是默认生成的
var a=44
get(){
return field
}
set(value) {
field=value
}
}

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var al:Animal=Animal(1)
var a2:Animal=Animal("dd")
var bl:Animal=Animal1(1)
var b2:Animal=Animal1("dd")
//构造方法不加val或者var 相当于内部属性 非成员变量
class Animal constructor(var name:String) {//主构造方法还可以直接定义变量
var age:Int=0
init{
println("主构造方法$name")
}
//定义了主构造方法后 次构造方法要调用主构造方法
constructor(age:Int) : this("") {
this.age=age
println("次构造方法$age")
}
}
class Animal1 {
constructor(name:String){
println("不分主次$name")
}
constructor(age:Int){
println("不分主次$age")
}
}

伴生对象(静态方法静态成员)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Animal1 {
companion object {// companion声明伴生对象
//如果加上这个注解 暴露为静态方法 java才可以直接像静态变量使用
@JvmField
val NAME="HAHA"
var name:String?=null
//如果加上这个注解 暴露为静态方法 java才可以直接像静态方法使用
@JvmStatic
fun myFun(){
}
}
}
Animal1.myFun()
Animal1.name="ces"
Animal1 .NAME

继承

修饰符 说明
private 私有
protected 子类可见
internal 在同一个模块(module)内可以访问
public 任何地方可见

变量、方法、类默认都是public,所以可以把public省略,Kotlin的类默认是不能继承的(即final类型),如果需要继承某类,该父类就应当声明为open类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
open class A(name: String){
open fun a1(){// 加open才能重写
}
}
open class B (mname: String):A(mname){
override fun a1() {
super.a1()
}
}
//////////////////////////////////////////////////////////////////////////////
open class A(){
open fun a1(){// 加open才能重写
}
}
open class B :A(){
override fun a1() {
super.a1()
//指定类写法
super<A>.a1()
}
}

抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Animal1(override var str: String?,var str1:String) : AbstractAnimal1(str1) {
override fun a1() {
}
override fun a2(int: Int): String {
return ""
}
}
open class Animal3 {
constructor(a3:String){
println("不分主次$a3")
}
constructor(a4:Int){
println("不分主次${a4.toString()}")
}
}
//: Animal3(a3)相当于在构造函数中super(a3)
abstract class AbstractAnimal1(a3: String) : Animal3(a3) {
var yyy:String="dd"//非抽象属性
abstract var str:String?//抽象属性
//抽象方法
abstract fun a1()//bstract方法默认就是open类型
abstract fun a2(int: Int):String
//非抽象方法
fun a3(){
}
}

接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
open class A():B {
override fun update(): Boolean {
return true
}
override fun push(s: String) {
printLog()
}
}
interface B{
//接口内部的方法和属性默认就是抽象的可以不加abstract和open
fun push(s:String)
fun update():Boolean
fun printLog(){//可以定义已实现的方法
print("print")
}
}
//////////////////////////////////接口回调写法////////////////////////////////////////////
//1.java思维
open class A {
var b:B?=null
open fun setListener(b:B){
this.b=b
}
}
interface B{
//接口内部的方法和属性默认就是抽象的可以不加abstract和open
fun push(s:String)
}
class C{
fun main() {
var a=A()
a.setListener(object:B{
override fun push(s: String) {
}
})
}
}
//2.kotlin思维 匿名函数
open class A {
//(参数列表类型) -> 返回值类型 Unit 为无返回值
lateinit var mListen: (String)-> Unit
lateinit var mListen1: (String,Int)-> Unit
lateinit var mListen2: (String,Int)-> String
open fun setListener(mListen: (String)-> Unit){
this.mListen=mListen
this.mListen.invoke("mListen")
}
open fun setListener1(mListen1: (String,Int)-> Unit){
this.mListen1=mListen1
this.mListen1.invoke("mListen1",1)
}
open fun setListener2(mListen2: (String,Int)-> String){
this.mListen2=mListen2
this.mListen2.invoke("mListen1",1)
}
}
class C{
fun main(){
var a=A()
a.setListener { println(it) }
a.setListener1{a,b->
print(a)
print(b)
}
a.setListener2 { s, i ->if (s=="")
"" else "haha"
}
a.setListener2 { s, i ->
print(s)
print(s)
"d"
}
}
}

接口代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
//------------------------------------------------
class Derived(b: Base) : Base by b{
fun myprint(){
print()
}
}
//java式写法
class Derived1(var b: Base) : Base{
override fun print() {
b.print()
}
}
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print()
Derived1(b).print()
}

内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
open class A {//用inner修饰内部类的时候新建的是非静态内部类
var name:String?=null
inner class B{
fun setName(){
name="hah"
}
}
}
open class C{//默认状态新建的内部类是静态内部类
companion object {
var name:String?=null
}
class D{
fun setName(){
name="hah"
}
}
}
class F{
fun main(){
var b=A().B().setName()
var c=C.D().setName()
}
}

枚举

延迟加载

lateinit修饰变量var

lateinit不能对可空类型使用 例如Int 那些

不能拥有自定义 get set

在调用lateinit修饰的变量时,如果变量还没有初始化,则会抛出未初始化异常

1
2
3
4
5
6
7
// 声明一个string变量
lateinit var a1: String
private fun test() {
// 初始化
a1 = "test1"
}

lazy修饰常量val

lazy的加载时机为第一次调用常量的时候,且只会加载一次(毕竟是个常量,只能赋值一次)

1
2
3
4
5
val a2:String by lazy{
println("开始初始化")
// 初始化的值
"sss"
}

拓展函数/属性

定义在类外部的类函数

拓展的方法和类的方法 重名同参数数量的话 优先度低不会执行拓展函数 拓展的方法能访问拓展类的成员

1
2
3
4
5
6
7
8
9
fun String.lastChar(): Char = this.get(this.length - 1)
val String.hahah:String
get()="111"
fun test() {
println("Kotlin".lastChar())
}

匿名函数

定义一个简单的函数可以用匿名函数

匿名函数,没有名称的函数,只定义参数列表、返回值类型和函数体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//表示一个函数的类型
//(参数列表类型) -> 返回值类型
//定义一个函数变量 没有赋值 需要赋值才能使用
val sum:(Int,Int)->Int
//这种是没类型声明的
val t1 = fun(x:Int,y:Int) :Int{
return x+y
}
val t2 = fun(x:Int,y:Int) :Int =x+y
val t3 = fun(x:Int,y:Int) =x+y
//这种是有类型声明
val t4:(Int,Int) -> Int = fun(a:Int,b:Int) = a+b
//Lambda 表达式(lambda expression)是一个匿名函数
//Lambda表达式的语法结构:
//{参数名1: 参数类型, 参数名2: 参数类型 -> 函数体}
//函数体中可以编写任意行代码(虽然不建议编写太长的代码),并且最后一行代码会自动作为Lambda表达式的返回值。
val t4={x:Int,y:Int->x+2;x+3 ;x+y}
val t5={x:Int,y:Int->x+y}

高阶函数

双冒号操作符 表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。

为了防止作用域混淆 , :: 调用的函数如果是类的成员函数或者是扩展函数,必须使用限定符,比如this::方法名

将函数用作函数参数的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
//调用t1 函数
val a1:Int=t1(1,::aa)
//lambda写法
val a2:Int=t1(1) { x:Int, y:Int->x+y}
---------------------------------------
fun aa(c1:Int,c2:Int):Int{
return c1+c2
}
fun t1(a:Int,c:(c1:Int,c2:Int)->Int):Int{
return c(1,a)
}

内联函数

文本替换,每个使用到的地方都会替换为内联函数。
内联函数是在编译期间起作用,还没到运行期间,不会造成函数调用的开销(开辟栈空间,记录返回地址,将形参压栈,从函数返回还要释放堆 和c的内联一样

1
2
3
4
5
inline fun inlineFunc(prefix : String, action : () -> Unit) {
println("call before $prefix")
action()
println("call after $prefix")
}

协程

1
2
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.6"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.6"

启动一个协程可以使用 launch 或者 async 函数 async 函数相当于带执行结果的launch
runBlocking 创建的协程直接运行在当前线程上,同时阻塞当前线程直到结束

调度器

Dispatchers.Main:Android 中的主线程
Dispatchers.IO:针对磁盘和网络 IO 进行了优化,适合 IO 密集型的任务,比如:读写文件,操作数据库以及网络请求
Dispatchers.Default:适合 CPU 密集型的任务,比如计算

挂起函数

suspend关键字修饰的函数,我们称之为挂起函数。挂起函数只能从协程代码内部调用,普通的非协程的代码不能调用 ,挂起函数在执行完成之后,协程会重新切回它原先的线程。

1
2
3
4
5
suspend fun suspendingGetImage(id: String) = withContext(Dispatchers.IO) {
...
}
//例如 suspendingGetImage 在main线程执行 会在withContext线程执行完后 才回到main线程执行下面的代码 而且不会卡主线程

父协程需要用到子协程的结果 有三种写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GlobalScope.launch( Dispatchers.IO) {
val a=GlobalScope.launch( Dispatchers.IO) {
}
a.join()//挂起 会让a线程执行完再执行下面代码
a.cancel()//取消线程写法
val b=GlobalScope.async ( Dispatchers.IO) {
return@async 100
}
var b2=b.await()//挂起 会让b线程执行完再执行下面代码 带返回值
//相当于省略 await() 的async 自动挂起
var c = withContext(Dispatchers.IO){
return@withContext 300
}
}