发布于2022年11月4日3年前 一、Kotlin基础 1.数据类型声明 在Kotlin中要定义一个变量需要使用var关键字 //定义了一个可以修改的Int类型变量 var number = 39 如果要定义一个常量可以使用val关键字,等价于Java的final关键字. val name = "miku" //给val定义的常量再次赋值就会提示错误 name = "ミク" 在Kotlin中要声明一个数据类型必须要使用var或者val来定义. 2.数据类型 Kotlin的数据类型分为基本数据类型和引用数据类型. 基本数据类型:Boolean、Number、Char 引用类型:可空类型、Object、数组类型 Kotlin中的Number类型泛指所有跟数字有关的类型,int、float、double、long //java.lang.Integr Int--->int var intNumber:Number = 39 //java.lang.Float Float--->float var floatNumber:Number = 39.0f //java.lang.Double Double--->double var doubleNumber:Number = 39.000000000000000 //java.lang.Long Long--->long var longNumber:Number = 39L 2.1.Kotlin预定义的基本数据类型 var intType:Int = 39 //int var shortType:Short = 39 //short var byteType:Byte = 0 //byte var boolType:Boolean = true //boolean var stringType:String = "miku" //String var floatType:Float = 39f //float var doubleType:Double = 39.0000000000 //double var longType:Long = 39L //long var numberType:Number = 39 //Integr、Float、Double、Long var charType:Char = '0' //char 字符串拼接,通过${}直接引用变量,不再需要引号 + 引号的方式 val name = "komine" val text = "私の名前は${name}と申します。" print(text) 2.2.元组 元组是一个固定长度,且不能被修改的数据结构 //二阶元组 var person1 = Pair("komin",16) //三阶元组 var person2 = Triple("komine",18,"女") person2.first //第一个元素的值 person2.second //第二个元素的值 person2.third //第三个元素的值 Kotlin的元组其实就是一个泛型对象的封装.通过反编译生成的.class文件可以看出,二阶元组其实是对应Pair类, 三阶元组其实是对应Triple类. first、second、third分别对应getFirst()、getSecond()、getThrid()方法. 如果用Java来实现的话差不多就是下面这个样子,可以看到,元组不过是Kotlin在源码阶段提供的一种更方便的语法而已. public class MyPair<First,Second> { private final First mFirstValue; private final Second mSecond; public MyPair(First first, Second second){ this.mFirstValue = first; this.mSecond = second; } public First getFirst() { return mFirstValue; } public Second getSecond() { return mSecond; } } 2.3.可空类型 Kotlin定义了一种可以为空的数据类型,只有声明为可空类型的变量才能将null赋值于它.默认声明的类型全部都是非空类型. var str:String? = null //在类型的后面添加?表示该类型可空 print(str?.length) //通过?.方式来访问可空类型的成员方法或变量,如果str是null则后面的调用会返回null,不会报错 //如果你明确知道,str在某个时候肯定不为空,可以通过!!操作该变量,但如果str为空则会抛出空指针异常 print(str!!.length) 2.4.数组 在Kotlin中定义一个数组非常简单.调用arrayOf()即可创建一个数组. var names = arrayOf("miku","rin","luka") var numbers = arrayOf(16,14,17) //跟Java一样,也可以通过[下标]的方式访问数组的元素 print(names[0]) //创建一个指定长度的数组,值为null var fixedArray = arrayOfNulls<Int>(39) //创建一个空数组 var emptyArray = emptyArray<Int>() 2.5.集合 Kotlin的集合分为可变集合和不可变集合.可变集合都是由Mutable开头的. 可变集合 //MutableList集合,该集合可以包含相同元素 var mutableList = mutableListOf("miku","rin","luka") //键值对,包含Key和Value的集合 var mutableMap = mutableMapOf<String,String>() var linkedHashMap = linkedMapOf<String,String>() //等价mutableMap //MutableSet集合,该集合不会出现相同的元素,如果集合已经包含某个值,添加的时候会忽略添加操作 var mutableSet = mutableSetOf<Object>() var linkedSet = linkedSetOf<String>() //等价mutableSet 不可变集合 //List集合,无法进行添加操作 var list = listOf("miku","rin","luka") //Map集合,无法进行添加操作 var map = mapOf(Pair("miku",16)) var linkedMap = //Set集合,无法进行添加操作 var set = setOf<Int>(0,1,2,3,4,5,6,7,8,9) 2.5.1扩展方法 toList() 返回一个不可变List集合 toMap() 返回一个不可变的Map集合 toSet() 返回一个不可变的Set集合 2.5.2集合操作 类似.Net中的Linq查询 any 判断集合中是否有满足条件的元素 var mutableList = mutableListOf("miku","rin","luka") val result:Boolean = mutableList.any(){ it == "miku" //有一个元素的值为miku } all 判断集合中的所有元素是都否满足条件 var mutableList = mutableListOf("miku","rin","luka") val result:Boolean = mutableList.all(){ it.isNotEmpty() //元素的长度大于0 } none 判断集合中的所有元素是否都不满足条件,满足则返回true var mutableList = mutableListOf("miku","rin","luka") val result:Boolean = mutableList.none(){ it.isNotEmpty() //元素的长度等于0 false } count 返回满足条件的元素个数,类似sql中的select count(*) from table where xx = xx var mutableList = mutableListOf("miku","rin","luka") val result: Int = mutableList.count { it == "miku" //返回集合中元素值为miku的元素个数 } reduce 从第一个元素到最后一个元素的累加 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result: Int = numbers.reduce() { sum: Int, i -> sum + i //sum:声明一个用于接收累加结果的变量 i->:循环每一个元素,sum + i:累加每个元素到sum } reduceRight 从最后一个元素到第一个元素的累加 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result: Int = numbers.reduceRight() { sum: Int, i -> sum + i } fold 跟reduce类似,不过可以设置初始化,从初始值开始累加 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result: Int = numbers.fold(10){ sum, i -> sum + i } foldRight... sumOf 返回集合中所有元素的总和,该集合的元素必须是Number类型 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result = numbers.sumOf{ it } dropWhile 去除满足条件的元素,直到不满足为止,返回剩余元素的集合 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result = numbers.dropWhile { it < 5 } filter 过滤不满足条件的元素,返回满足元素的新集合 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result = numbers.filter { it < 5 } filterNot 过滤满足条件的元素,返回不满足元素的新集合 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result = numbers.filterNot { it < 5 } take 返回从第一个元素开始的N个元素 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result = numbers.take(5) //{1,2,3,4,5} takeLast 返回从最后一个元素开始的N个元素 ... takeWhile 返回从第一个元素开始符合给定条件的元素 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result = numbers.takeWhile { it > 0 } drop 返回去掉N个元素之后的列表 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result = numbers.drop(1) dropLastWhile 返回从最后一个元素开始,去掉满足条件的元素 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result = numbers.dropLastWhile { it > 5 } slice 保留指定下标对应的元素 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result = numbers.slice(listOf(1,2,3)) map 将集合中的元素通过某种方法转换后,返回一个新集合 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) val result = numbers.map { val diff = 10 it * diff } 后续更新... 2.6.类型转换 Kotlin通过as关键字将一个类型转换为另一个类型. var numberType:Number = 39 var intType:Int = numberType as Int Kotlin可以通过 is 关键字自动完成装箱的操作. open class GameConsole{ } class PS4 : GameConsole() { } class NintendoSwitch : GameConsole() { public val version = "10.0" } fun main(args: Array<String>) { val gameConsoles = arrayOf(PS4(),NintendoSwitch()) val gameConsole = gameConsoles[1] //会自动完成装箱操作,在当前调用范围内将gameConsole识别NitendoSwitch,可以直接访问对象的成员,不需要手动转换 if(gameConsole is NintendoSwitch){ print(gameConsole.version) } } 3.运算符 Kotlin特有的运算符有===、!== 3.1.恒等和非恒等 判断两个对象之间的地址是否相等 var obj1 = Object() var obj2 = Object() if(obj1 === obj2){ println("恒等") } if(obj1 !== obj2){ println("非恒等") } 3.2.位运算 只有Int和Long类型可以使用. val i:Int = 10 val j:Int = 20 val result = i shl j //有符号左移 result = i shr j //有符号右移 result = i ushr //无符号右移 result = i and j //按位与 result = i or j //按位或 result = i xor //按位异或 result = i inv j //按位取反 3.3.区间运算符 表示某个值是否在某个范围或者集合之中. //i >=0 && i <= 100 if(i in 0..100){ print(i) } 数组或者集合是否包含某个值 val names = arrayOf("miku","rin","luka") if("miku" in names){ print("包含") } 4.条件语句 when是Kotlin提供类似Java中Switch的条件语句.它能做到Switch能做到的所有事,并且还提供了更方便的语法. fun test(i:Int){ when(i){ // case 1 1 ->{ } //case 2 2 -> { } //i >= 3 && i <= 9 in 3..9 ->{ } //default else ->{ } } } when如果不提供参数也可以当作if elseif使用 5.循环语句 跟Java的使用差别不大,一般配合区间运算符in来更简便的使用 val names = arrayOf("miku","rin","luka") for (name in names){ println(name) } //遍历数组或集合带索引 for ((index,name) in names.withIndex()){ } val i = 0 for (i in 0..99){ //i = 0;i <= 99;i++ println(i) } var i = 10 while (i > 0){ println(i) i-- } do { i-- println(i) }while (i > 0) //foreach val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) numbers.forEach{ it -> println(it) } //foreach 带索引 val numbers = mutableListOf(1,2,3,4,5,6,7,8,9) numbers.forEachIndexed{index, i -> println("index[${index}] = $i") } 6.函数 Kotlin中定义函数使用fun关键字.如果方法不需要返回值可以不写,或者写Unit fun sum(number1:Int,number2:Int):Int{ return number1 + number2 } 6.1默认参数 Kotlin中添加了默认参数的支持,当一个方法指定了参数的默认值,则调用的时候可以不提供该参数的值. 要使用默认参数,在参数的后面添加=进行赋值操作. fun sum(number1:Int,number2:Int = 0):Int{ return number1 + number2 } 6.2可变参数 和Java一样,Kotlin也提供可变参数的支持,使用vararg关键字声明可变参数 fun sum(vararg number:Int){ //number在使用的时候其实是一个数组类型的变量,可以调用数组的一些方法 } 6.3Lambda表达式 Lambda表达式可以看作是一个匿名的函数. var execute:(Int,Int) -> Int = {x,y -> x * y } println(execute(10,10)) 如果函数只有一个参数时可以省略不写,这个时候用it来表示 var execute:(String) -> String = { it } println(execute("komine")) 6.4高阶函数 Kotlin支持将函数作为参数或者返回值,包含这样操作的函数称为高阶函数.将函数作为参数时使用双冒号::来传递. fun main(args: Array<String>) { call(::method) } fun call(m:(number:Int) -> Int){ println(m(39)) } fun method(number:Int):Int{ return number * number } 也可以使用Lambda表达式来表示一个匿名参数. call{number: Int -> return@call number * number } Kotlin本身也提供了一些高阶函数供我们使用,比如apply函数,在Android中初始化变量可以这样写. var paint = Paint().apply { this.isAntiAlias = true this.color = Color.BLACK this.style = Paint.Style.STROKE this.strokeWidth = 10f } 6.5内联函数 Kotlin支持内联函数,跟C++的内联函数作用一致,因为函数的执行有压栈和出栈的步骤,会带来一定的开销. 在将函数声明为内联函数的时候,在编译的时候,编译器会在所有调用函数的地方,将函数调用直接替换成函数体的内容. 一般来说,内联函数中的嵌套逻辑不能太复杂,C++的内联函数是否替换是由编译器决定的,Kotlin会按照inline关键直接替换. 通过反编译生成的class文件可以看到,内联函数就是直接将有函数调用的地方,直接替换成函数体的内容. fun main(args: Array<String>) { test() } inline fun test(){ for (i in 0..99){ println(i) } for (i in 0..99){ println(i) } for (i in 0..99){ println(i) } for (i in 0..99){ println(i) } for (i in 0..99){ println(i) } for (i in 0..99){ println(i) } } 反编译结果 如果函数的参数中有函数参数类型或者Lambda表达式,也可以使用noinline关键字指定不参数内联的函数. crossinline关键字待补充... 二、Kotlin进阶 1.协程 协程是跑在线程上的产物,它拥有自己的栈内存和局部变量,被称为轻量级Thread.它的内部实现是由编译器来完成的. 官方文档说明 在使用协程之前,需要添加依赖 implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1' 简单用法 GlobalScope.launch(context = Dispatchers.Default, start = CoroutineStart.DEFAULT) { //延时1.5秒 delay(1500L) println("当前线程:" + Thread.currentThread().name) println("World") } println("Hello,") println("当前线程:" + Thread.currentThread().name) Thread.sleep(3000L) context:协程的上下文,这里设置的是CoroutineDispatch协程运行的线程调度器,它有四种线程模式: Dispatchers.Default //默认 Dispatchers.IO //工作在其他线程 Dispatchers.Main //主线程 Dispatchers.Unconfined //不指定就是在当前线程,kotlinx.coroutines.DefaultExecutor 也可以自己创建一个协程上下文,这个上下文也可以理解为协程所运行在的线程. val context = newSingleThreadContext("single") start:启动模式,默认是CoroutineStart.DEFAULT,就是创建之后就会启动 CoroutineStart.DEFAULT CoroutineStart.ATOMIC CoroutineStart.UNDISPATCHED CoroutineStart.LAZY 懒加载模式,它会返回一个Job对象,你可以手动开启它. val job = GlobalScope.launch(context = Dispatchers.Default, start = CoroutineStart.LAZY) { //延时1秒 delay(1500L) println("当前线程:" + Thread.currentThread().name) println("World") } println("Hello,") println("当前线程:" + Thread.currentThread().name) job.start() Thread.sleep(3000L) GlobalScope.async 带返回值 suspend fun main(args: Array<String>) { val result = GlobalScope.async { delay(1000L) return@async "async" } println(result.await()) Thread.sleep(3000) } async会阻塞当前协程,会等待当前协程执行完毕,调用await()的函数需要使用suspend关键字修饰. 协程的挂起,suspend表示当前协程被挂起. fun main(args: Array<String>) { GlobalScope.launch { //get方法是被suspend修饰的,表示调用时会将当前协程挂起 val str = get() //会等待get()执行完毕才会继续执行 printStr(str) } //防止进程结束 Thread.sleep(3000L) } suspend fun get():String{ println("get()正在执行..") delay(1000) return "data" } suspend fun printStr(str:String){ println(str) } 协程之间也可以嵌套,调用await会阻塞外部协程,代码还是会按顺序运行 fun main(args: Array<String>) { GlobalScope.launch { val str = GlobalScope.async { return@async get() }.await() GlobalScope.launch{ printStr(str) } } //防止进程结束 Thread.sleep(3000L) } suspend fun get():String{ println("get()正在执行..") delay(1000) return "data" } suspend fun printStr(str:String){ println(str) } 待补充... 2.面向对象 2.1类的声明 和很多语言一样,Kotlin使用class关键字来声明一个类. class Person{ } 2.1.1内部类 class Person{ //内部类的声明使用inner关键字修饰 inner class Info{ } } 要实例内部类需要先实例化主类 val person = Person("miku") val info = person.Info() 2.1.2数据类 通过data class 修饰的类称为数据类,数据类必须提供一个有参的构造函数.数据类一般不定义方法. data class MyData(val height:Float,val weight:Float,val money:Float){ } 2.1.3枚举类 跟Java的枚举类使用基本相同. enum class DirectionEnum{ East{ override fun move() { //name:当前枚举常量的名称 //ordinal:当前枚举常量的值 println("name$name") println("value:$ordinal") } }, South{ override fun move() { } }, West{ override fun move() { } }, North{ override fun move() { } }; abstract fun move(); } 2.1.4密封类 emm...不知道怎么用,后面补充 2.1.5抽象类 使用abstract关键字声明一个抽象类 open abstract class Person(name:String, age:Int){ } 2.2构造函数 Kotlin的构造函数可以直接写在类名的后面,称为主构造函数,这样定义的构造函数是没有方法体的,要执行初始化操作 可以使用init代码块. open class Person(name:String, age:Int){ init { println("主构造函数执行...") println("name:${name},age = $age") } } 2.2.1 副构造函数 通过constructor定义的构造函数称为副构造函数,需要间接调用主构造函数进行初始化 fun main(args: Array<String>) { val person = Person("miku") } open class Person(name:String, age:Int){ init { println("主构造函数执行...") println("name:${name},age = $age") } //副构造函数需要间接调用主构造函数通过this关键字 constructor(name:String) : this(name,16) { println("副构造函数执行...") } } 2.2.2 构造函数私有化 如果不想类外部访问到类的构造函数,可以使用 private constructor来修饰主构造函数 class ListDialog<T>private constructor(context: Context) :Dialog(context) { } 2.3继承 如果想让一个类可以被其他类继承,需要在类的声明之前加上open关键字,使用:冒号来继承 open class Person{ open fun sodayo(){} } class Student : Person() { } 如果子类想重写父类定义的方法,该方法必须是open关键字修饰的方法,抽象方法的修饰符默认是open. 2.4静态成员 Kotlin没有提供static关键字,如果想实现Java那样的静态成员调用可以使用companion object代码块来定义静态成员. 在Kotlin中称为伴生对象,用伴生对象的成员来代替静态成员. class PS4{ companion object{ val FIRMWARE_VERSION:Float = 10.0f fun boot(){ println("boot...") } } } 跟Java一样,然后通过类名调用静态成员 PS4.boot() PS4.FIRMWARE_VERSION 2.5接口 使用interface来声明一个接口.与Java的用法并无二致 interface Callback{ fun onSuccess() fun onFailed() } 3.泛型 基本使用和Java一致 class Data<T>(private val data:T){ fun get():T{ return data } } val data1 = Data("String") println(data1.get()) val data2 = Data(16) println(data2.get()) 3.1out和in关键字 Kotlin中使用out和in来代替? extends 和? super使用,具体用法跟Java是类似的. fun main(){ val ps4List = mutableListOf<PS4>() setGameConsoles(ps4List) val gameConsoles = getGameConsoles() for (gameConsole in gameConsoles){ val ps4 = gameConsole as PS4 ps4.play() } } // out ----> ? extends GamesConsole fun setGameConsoles(gamConsoles:MutableList<out GameConsole>){ } //in -----> ? super PS4 fun getGameConsoles():MutableList<in PS4>{ val gameConsoles = mutableListOf<GameConsole>() gameConsoles.add(PS4()) gameConsoles.add(PS4()) return gameConsoles } open class GameConsole{ open fun play(){ } } class PS4 : GameConsole(){ override fun play() { println("ps4 play...") } } class NintendoSwitch : GameConsole(){ override fun play() { println("ns play...") } } 4.委托 其实就是代理设计模式. 4.1委托的使用场景 Java中的代理模式实现 //支付接口 public interface IPay { void pay(); } //支付宝支付 public class AliPay implements IPay{ private float mMoney; public AliPay(float money){ mMoney = money; } @Override public void pay() { Log.d("Alipay","支付宝支付" + mMoney + "元..."); } } //微信支付 public class WeChatPay implements IPay{ private float mMoney; public WeChatPay(float money){ mMoney = money; } @Override public void pay() { Log.d("WeChatPay","微信支付" + mMoney + "元..."); } } //代理对象 public class ProxyPay implements IPay{ private IPay mPay; public ProxyPay(IPay pay){ mPay = pay; } @Override public void pay() { mPay.pay(); } } Kotlin的代理模式实现 interface IPay{ fun pay() } class Alipay(private val money:Float) :IPay{ override fun pay() { println("支付宝支付$money...") } } class WeChatPay(private val money: Float) :IPay{ override fun pay() { println("微信支付$money...") } } class PayDelegate(private val play:IPay) :IPay by play{ //什么都不需要做,系统帮我们完成一系列操作 } 可以看出,Kotlin的委托其实就是简化了代理模式的实现过程. 4.2属性委托 将属性的赋值操作交给其他类来代理.可以通过其他类来统一控制属性的取值,合法性等等操作. 基本使用 //声明一个接口,不一定要接口也可以是一个类 interface IPropertyDelegate class PS4 :IPropertyDelegate{ var version:String by DataDelegate() } class NintendoSwitch :IPropertyDelegate{ var version:String by DataDelegate() } class DataDelegate { private var version:String = "" operator fun setValue(thisRef: IPropertyDelegate, property: KProperty<*>, value: String) { this.version = value } operator fun getValue(thisRef: IPropertyDelegate, property: KProperty<*>): String { return this.version } } 当给PS4或者NintendoSwitch对象的version赋值的时候,就会去到DataDelegate对象的setValue()方法,达到统一赋值的操作,相同的操作不需要在每个类都写一次 val ps4 = PS4() ps4.version = "7.55" println(ps4.version) val ns = NintendoSwitch() ns.version = "13.0" println(ns.version) 5.其他 5.1扩展函数 给某个类添加一个扩展函数,其效果跟成员函数调用一致.一般定义到一个统一的文件中,不需要先定义一个类型,直接写就好 //给String扩展了一个first方法 fun String.first():Char{ return this[0] } println("komine".first()) 5.1扩展属性 给某个类添加一个扩展属性 val Int.dp:Int get(){ //简单模拟一下,开发中不是这么计算的 return this * 1.5f.toInt() } 比如给Int类型添加了一个dp的扩展属性,可以将int值转换为对应的dp val width = 39.dp 待更新...
创建帐户或登录后发表意见