Kotlin基础

Kotlin语言声明变量与内置数据类型

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Kotlin 语言声明变量与内置数据类型

fun main() {
println("Hello World")
var name: String = "Derry"
/*
可读可改 变量名 类型 值
var name : String = "Derry"
*/
name = "Lance"
println(name)
//内置数据类型
/*
String 字符串
Char 单字符
Boolean true/false
Int 整型
Double 小数
List 集合
Set 无重复的元素集合
Map 键值对集合
*/
}

Kotlin语言的只读变量

代码:

1
2
3
4
5
6
fun main(){
//只读变量
//变量永远不会被修改,建议修改为 val == 不可修改的(只读)
val info :String ="MyInfo"
println(info)
}

Kotlin语言的类型判断

代码:

1
2
3
4
5
6
7
8
9
10
fun main()
{
//类型判断
//提示:Explicitly given type is redundant here
//给定的类型在这里是多于的
val info :String = "Derry is Success"
println(info)
val age = 98
println(age)
}

Kotlin语言的编译时常量

代码:

1
2
3
4
5
6
const val PI=3.14 //定义编译时常量
//编译时常量只能在函数之外定义,因为如果在函数之内定义,就必须在运行时才能调用函数赋值,何来编译时常量一说
fun main(){
//编译时常量 只能是常用的基本数据类型 (String,Double,Int,Float,Long,Short,Byte,Char,Boolean)
//修饰符const不适用于局部变量
}

查看kotlin反编译字节码

代码:

1
2
3
4
5
6
7
8
9
10
const val PI1 = 3.1415
const val PI2 = 3.1415
const val age = 99

//查看kotlin反编译字节码
fun main() {
val name = "Derry"
val sex = '男'
val number = 108
}

Tools->Kotlin->Show Kotlin Bytecode

kotlin引用类型学习

1
2
3
4
5
6
7
//kotlin引用类型学习
fun main() {
val age: Int = 99//引用类型
val pi: Float = 3.1415f
val pi2: Double = 3.1415
val isOk: Boolean = true
}

range表达式

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//kotlin range表达式
fun main() {
val number = 148
//range 范围 从哪里 到哪里
if (number in 10..59) {
println("不及格")
} else if (number in 0..9) {
println("建议remake")
} else if (number in 60..100) {
println("及格")
} else if (number !in 0..100) {
println("不正确")
}
}

when表达式

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//kotlin when表达式
fun main() {
val week = 5
val info = when (week) {
1 -> "星期一"
2 -> "星期二"
3 -> "星期三"
4 -> "星期四"
5 -> "星期五"
6 -> "星期六"
7 -> "星期天"
else -> "错误天数"
}
println(info)
}

String模版

代码:

1
2
3
4
5
6
7
8
9
//kotlin String模版
fun main() {
val garden = "黄石公园"
val time = 6
println("今天天气很晴朗,去${garden}玩,玩了$time 小时")
//kt的if是表达式,所以更灵活
val isLogin = true
println("server response result: ${if (isLogin) "恭喜你登录成功" else "登录失败"}")
}

kotlin语言函数头学习

代码:

1
2
3
4
5
6
7
8
9
//kotlin语言函数头学习
fun main() {
method01(99,"张三")
}
//函数默认为public
private fun method01(age:Int,name:String):Int{
println("你的姓名是$name,你的年龄是$age")
return 200
}

kotlin函数参数的默认参数

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fun main() {
method01("张三", 99)
method02("李四")
method03()
method03("赵六",89)
}

//函数默认为public
private fun method01(name: String, age: Int): Int {
println("你的姓名是$name,你的年龄是$age")
return 200
}

private fun method02(name: String, age: Int = 18): Int {
println("你的姓名是$name,你的年龄是$age")
return 200
}

private fun method03(name: String="王五", age: Int = 18): Int {
println("你的姓名是$name,你的年龄是$age")
return 200
}

kotlin语言的具名函数参数

代码:

1
2
3
4
5
6
7
8
//kotlin语言具名函数参数
fun main() {
loginAction(age = 99,userPassword = "123",userName = "张三",phoneNumber = "123456")
}
private fun loginAction(userName:String,userPassword:String,phoneNumber:String,age:Int)
{
println("userName:$userName,userPassword:$userPassword,phoneNumber:$phoneNumber,age:$age")
}

kotlin语言Unit函数特点

代码:

1
2
3
4
5
6
7
8
9
10
11
12
//:Unit 默认有 
private fun doWork(): Unit {
return println()
}

private fun doWork1() {
return println()
}

private fun doWork2() {
println()
}

上面三个是等效的

kotlin语言的Nothing类型特点

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fun main() {
show(99)
}

private fun show(number: Int) {
when (number) {
-1 -> TODO("没有该分数")
in 0..59 -> println("不及格")
in 60..70 -> println("一般")
in 71..80 -> println("良好")
in 81..100 -> println("优秀")
}
}
interface A{
fun show()
}
class AImpl:A{
override fun show() {
//下面这句话不是注释提示,会终止程序的
TODO("Not yet implemented")
}
}

kotlin语言的反引号中函数名特点

代码:

1
2
3
4
5
6
7
8
9
10
11
fun main() {
//第一种情况:
`登录功能 2022122号`("张三","123456")
//第二种情况 in is
}
private fun `登录功能 2022年1月22号`(name:String,pwd:String){
println("测试:姓名:"+name+" 密码:"+pwd)
}
private fun `7626837269`(){
//加密: 7626837269
}

匿名函数

代码:

1
2
3
4
5
6
7
8
fun main() {
val len = "Jack".count()
println(len)
val count = "Jack".count() {
it == 'c'
}
println(count)
}

函数类型和隐式返回

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun main() {
//1.函数输入输出的声明
val methodAction: () -> String
//2.实现函数
methodAction = {
val inputValue = 999999
"$inputValue Jack"
//匿名函数不用写return,最后一行就是返回值
}
//3.调用函数
println(methodAction())
}
/*
fun methodAction():String{
return "Jack"
}
*/

函数参数

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
//1.函数声明和实现
val methAction: (Int, Int, Int) -> String = { number1, number2, number3 ->
val inputValue = 999999
"$inputValue Jack 参数1:$number1,参数2$number2,参数3:$number3"
}
//2.调用函数
println(methAction(1,2,3))
}
/*
fun methAction(number1:Int,number2: Int,number3: Int):String{
val inputValue = 999999
return "$inputValue Jack 参数1:$number1,参数2$number2,参数3:$number3"
}
*/

it关键字特点

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main() {
val methodAction :(Int,Int,Int)->String ={ n1,n2,n3->
val number =673;
println("$number n1:$n1,n2:$n2,n3:$n3")
"$number n1:$n1,n2:$n2,n3:$n3"
}
//methodAction.invoke(1,2,3)
methodAction(1,2,3)
val methodAction2:(String)->String={"$it xxx"}
println(methodAction2("AAAA"))
val methodAction3:(Double)->String ={"$it xxx"}
println(methodAction3(678.67))
}

匿名函数的类型推断

1
2
3
4
5
6
7
8
9
fun main() {
//匿名函数 类型推断为String
//方法名:必须指定 参数类型和返回类型
//方法名 = 类型推断返回类型
val method1={v1:Double,v2:Float,v3:Int->
"v1:$v1,v2:$v2,v3:$v3"
}
println(method1(5687.78, 798.3f, 909))
}

lambda学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun main() {
//匿名函数 == lambda表达式
val addResultMethod = { number1: Int, number2: Int ->
"俩数相加的结果是:${number1 + number2}"
}//addResultMethod (Int,Int)->String
println(addResultMethod(1, 1))
val weekResultMethod={number:Int->
when(number){
1->"星期一"
2->"星期二"
3->"星期三"
4->"星期四"
5->"星期五"
6->"星期六"
7->"星期天"
else ->-1
}//weekResultMethod 函数: (Int)->Any
}
println(weekResultMethod(2))
//匿名函数属于lambda
}

在函数中定义参数是函数的函数

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
fun main() {
loginAPI("admin", "12345") { msg: String, code: Int ->
println("最终登录情况:msg:$msg,code:$code")
}
}

//模拟数据库sql
const val USER_NAME_SAVE_DB = "admin"
const val USER_PWD_SAVE_DB = "12345"

//登录API
fun loginAPI(username: String, userpwd: String, responseResult: (String, Int) -> Unit) {
if (username == null || userpwd == null) {
TODO("用户名或密码为空")//出现问题,终止程序
}
//校验
if (username.length > 3 && userpwd.length > 3) {
if (webServiceLoginAPI(username, userpwd)) {
//登录成功
responseResult("login success", 200)
} else {
//登录失败
responseResult("login error", 404)
}
} else {
TODO("用户名和密码不合格")//出现问题,终止程序
}
}

private fun webServiceLoginAPI(name: String, pwd: String): Boolean {
//kotlin中if为表达式
return name == USER_NAME_SAVE_DB && pwd == USER_PWD_SAVE_DB
}
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
fun main() {
//第一种方式
loginAPI2("admin", "12345", { msg: String, code: Int ->
println("最终登录情况:msg:$msg,code:$code")
})
//第二种方式
loginAPI2("admin", "12345", responseResult = { msg: String, code: Int ->
println("最终登录情况:msg:$msg,code:$code")
})
//第三种方式(推荐)
loginAPI2("admin", "12345") { msg: String, code: Int ->
println("最终登录情况:msg:$msg,code:$code")
}
}

//模拟数据库sql
const val USER_NAME_SAVE_DB2 = "admin"
const val USER_PWD_SAVE_DB2 = "12345"

//登录API
fun loginAPI2(username: String, userpwd: String, responseResult: (String, Int) -> Unit) {
if (username == null || userpwd == null) {
TODO("用户名或密码为空")//出现问题,终止程序
}
//校验
if (username.length > 3 && userpwd.length > 3) {
if (webServiceLoginAPI(username, userpwd)) {
//登录成功
responseResult("login success", 200)
} else {
//登录失败
responseResult("login error", 404)
}
} else {
TODO("用户名和密码不合格")//出现问题,终止程序
}
}

private fun webServiceLoginAPI(name: String, pwd: String): Boolean {
//kotlin中if为表达式
return name == USER_NAME_SAVE_DB2 && pwd == USER_PWD_SAVE_DB2
}

kotlin语言的函数内联

函数中使用lambda就应该使用内联 inline关键字,若函数没用使用lambda,就不需要声明成内联

如果此函数不使用内联,在调用端会生成多个对象来完成lambda的调用(性能损耗)

使用内联相当于C++ 中 #define 宏定义 宏替换 会把代码替换到调用处,调用处没有任何函数开辟,对象开辟的损耗

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
package derry.s1

fun main() {
loginAPI3("admin", "12345") { msg: String, code: Int ->
println("最终登录情况:msg:$msg,code:$code")
}
}

//模拟数据库sql
const val USER_NAME_SAVE_DB3 = "admin"
const val USER_PWD_SAVE_DB3 = "12345"

//登录API
inline fun loginAPI3(username: String, userpwd: String, responseResult: (String, Int) -> Unit) {
if (username == null || userpwd == null) {
TODO("用户名或密码为空")//出现问题,终止程序
}
//校验
if (username.length > 3 && userpwd.length > 3) {
if (webServiceLoginAPI3(username, userpwd)) {
//登录成功
responseResult("login success", 200)
} else {
//登录失败
responseResult("login error", 404)
}
} else {
TODO("用户名和密码不合格")//出现问题,终止程序
}
}

fun webServiceLoginAPI3(name: String, pwd: String): Boolean {
//kotlin中if为表达式
return name == USER_NAME_SAVE_DB3 && pwd == USER_PWD_SAVE_DB3
}

函数引用

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
fun main() {
//函数调用
//lambda属于函数类型的对象,需要将methodResponseResult普通函数变成函数类型的对象 函数引用(::)
// login("admin", "12345",::methodResponseResult)
val obj = ::methodResponseResult
val obj1 = obj
val obj2 =obj1
login("admin","12345",obj2)
}

fun methodResponseResult(msg: String, code: Int) {
println("最终登录情况:msg:$msg,code:$code")
}

//模拟数据库sql
const val USER_NAME_SAVE_DB4 = "admin"
const val USER_PWD_SAVE_DB4 = "12345"

//登录API
inline fun login(name: String, pwd: String, responseResult: (String, Int) -> Unit) {
if (USER_NAME_SAVE_DB4 == name && USER_PWD_SAVE_DB4 == pwd) {
//登录成功
responseResult("login success", 200)
} else {
//登录失败
responseResult("login error", 404)
}
}

函数类型作为返回类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fun main(){
val r = show("kotlin")
//r 是 show 函数的返回值
val showMethod_niming = showMethod("show")//返回匿名函数
println(showMethod_niming("程序员",38))
}
fun show(info:String):Boolean{
println("我是show函数 info:$info")
return true
}
fun show2(info:String):String{
println("我是show函数 info:$info")
return "XXX"
}
//showMethod函数 返回一个匿名函数
fun showMethod(info:String):(String,Int)->String{
println("我是show函数 info:$info")
//return 一个函数 匿名函数
return {name:String,age:Int ->
"匿名函数 name:$name, age:$age"
}
}

匿名函数与具名函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun main() {
//匿名函数
showPersonInfo("zhangSan", 90, '男', "学习kotlin语言") {
println("显示结果:$it")
}
//具名函数
showPersonInfo("liSi", 67, '男', "学习Java语言", ::showResultImpl)
}

fun showResultImpl(result: String) {
println("显示结果:$result")
}

inline fun showPersonInfo(name: String, age: Int, sex: Char, study: String, showResult: (String) -> Unit) {
val str = "name:$name,age:$age,sex:$sex,study:$study"
showResult(str)
}

Kotlin语言的可空性特点

不会出现空指针异常

1
2
3
4
5
6
7
8
9
10
fun main() {
//第一种情况:默认是不可空类型,不能随意给null
var name: String = "zhangSan"
//name = null 不能给空
println(name)
//第二种情况:声明时指定为可空类型
var name2: String?
name2 = null
println(name2)
}

Kotlin语言的安全调用操作符

1
2
3
4
5
6
7
fun main(){
var name:String?="zhangSan"
name =null
//name.capitalize() //name 是可空类型 可能是null,想要使用name必须给出补救措施 capitalize() 首字母大写
val r = name?.capitalize() //name 如果是null,?后面代码不执行,不会引发空指针异常
println(r)
}

在Kotlin语言中使用带let的安全调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main(){
var name :String ?=null
name = "zhangSan"
//name是可空类型,如果是null,?后面的代码不执行,不会发生空指针异常
var r = name?.let{
//it == name 本身
//如果能够执行到这里,it一定不为null
if(it.isBlank()){//如果name是空值 "" 没有内容
"Default"
}else{
"[$it]"
}
}
println(r)
}

Kotlin语言中的非空断言操作符特点

1
2
3
4
5
6
7
8
9
fun main(){
var name:String?=null
//name.capitalize() //name 是可空类型 可能是null,想要使用name必须给出补救措施 capitalize() 首字母大写
name = "zhangSan"
//补救措施 ?
val r = name!!.capitalize();//!!不管name是不是null都执行,和java一样
println(r)
//结论:如果100%保证name有值,那么才能使用断言!!,否则有空指针异常的风险
}

Kotlin语言中对比使用if判断null值情况

1
2
3
4
5
6
7
8
9
10
11
fun main() {
var name: String? = null
name = "zhangSan"
//name.capitalize() //name 是可空类型 可能是null,想要使用name必须给出补救措施 capitalize() 首字母大写
if (name != null) {//if也算是一种补救措施
val r = name.capitalize();
println(r)
} else {
println("name is null")
}
}

Kotlin语言空合并操作符

1
2
3
4
5
6
7
8
fun main() {
var info: String? = "李小龙"
//info = null
//空合并操作符 xxx?:"xxx" 如果xxx为null,执行?:后面的
println(info ?: "info is null")
//let函数+空合并操作符
println(info?.let { "[$it]" } ?: "info is null")
}

Kotlin语言中异常处理和自定义异常特点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.IllegalArgumentException

fun main() {
try {
var info: String? = null
checkException(info)
println(info!!.length)
} catch (e: Exception) {
println(e)
}
}

fun checkException(info: String?) {
info ?: throw CustomException()
}

class CustomException : IllegalArgumentException("你的代码出现了问题")

Kotlin语言先决条件函数

1
2
3
4
5
6
7
fun main(){
val value1:String ?= null
var value2:Boolean = false
//checkNotNull(value1) //java.lang.IllegalStateException: Required value was null.
//requireNotNull(value1)//java.lang.IllegalArgumentException: Required value was null.
//require(value2)//java.lang.IllegalArgumentException: Failed requirement.
}

substring

1
2
3
4
5
6
const val INFO = "Good good study,day day up!"
fun main(){
val indexOf = INFO.indexOf(',')
println(INFO.substring(0, indexOf))
println(INFO.substring(0 until indexOf))//0 until indexOf(kotlin使用此方式) 等价于 0, indexOf
}

split

1
2
3
4
5
6
7
8
9
10
fun main() {
val jsonText = "zhangSan,liSi,wangWu"
//list 自动类型推断成 list == List<String>
val list = jsonText.split(",")
//不解构 直接输出
println("分割后list集合里面的元素有$list")
//C++有解构 kt也有解构
val (v1, v2, v3) = list
println("解构3个只读变量的值为:v1:$v1,v2:$v2,v3:$v3")
}

Kotlin语言的replace完成加密解密操作

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
fun main(){
val sourcePwd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
println("原始密码:$sourcePwd")
//加密:就是把字符替换成数字 打乱 就属于加密了
val newPwd = sourcePwd.replace(Regex("[AKMNO]")){
//it.value //完全没有做任何事情
when(it.value){// A B C D ...
"A"->"9"
"K"->"3"
"M"->"5"
"N"->"1"
"O"->"4"
else ->it.value//返回字符本身
}
}
println("加密后:$newPwd")
//解密
val sourcePwdNew = newPwd.replace(Regex("[93514]")){
when(it.value){// A B C D ...
"9"->"A"
"3"->"K"
"5"->"M"
"1"->"N"
"4"->"O"
else ->it.value//返回字符本身
}
}
println("解密后:$sourcePwdNew")
}

==与===比较操作

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main() {
//== 值 内容的比较 相当于java中的equals
//=== 引用的比较
val name1 = "Jack"
val name2 = "Jack"
val name3 = "jack".capitalize()// Jack
//name1.equals(name2) 等价于 name1==name2 都是 值 内容的比较
println(name1.equals(name2))//java写法
println(name1 == name2)//kt写法
println(name1 === name2)//true
println(name1 == name3)
println(name1 === name3)
}

Kotlin语言的字符串遍历操作

1
2
3
4
5
6
7
8
9
10
11
fun main() {
val str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
str.forEach {
//it == str 的每一个字符 A B C D ...
print("$it ")
}
println()
str.forEach { c ->//覆盖默认的it参数名,相当于修改参数名为c
print("$c ")
}
}

Kotlin语言中数字类型的安全转换函数

1
2
3
4
5
6
7
8
9
10
11
fun main() {
val number: Int = "666".toInt()
println(number)
val number2: Int? = "666.6".toIntOrNull()
println(number2)
val number3: Int? = "666".toIntOrNull()
println(number3)
val number4: Int? = "666.6".toIntOrNull()
println(number4 ?: "无法转换,输出为null")
//字符串转换为整型时建议使用 toIntOrNull 方法
}

Kotlin语言中Double转Int与类型格式化

1
2
3
4
5
6
7
8
9
10
11
12
13
import kotlin.math.roundToInt

fun main(){
println(65.124543625.toInt()) //65
println(65.972674328.toInt()) //65
println(65.124654243.roundToInt()) //65
println(65.678937455.roundToInt()) //66
//toInt 直接去除小数部分
//roundToInt 四舍五入
//r 类型为 字符串 保留3位
val r = "%.3f".format(65.678937455)
println(r)
}

Kotlin语言的apply内置函数

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
import java.io.File

fun main(){
val info = "Welcome To My Class"
//普通的方式
println("info字符串的长度为:${info.length}")
println("info最后一个字符为:${info[info.length-1]}")
println("info全部转化小写为:${info.toLowerCase()}")
println()
//apply 内置函数
//info.apply返回值为info本身
val infoNew = info.apply {
println("apply匿名函数内:${this}")
println("info字符串的长度为:${length}")
println("info最后一个字符为:${info[length-1]}")
println("info全部转化小写为:${toLowerCase()}")
}
println("apply返回值:${infoNew}")

//apply函数真正写法:
info.apply {
println("info字符串的长度为:${length}")
}.apply {
println("info最后一个字符为:${info[length-1]}")
}.apply {
println("info全部转化小写为:${toLowerCase()}")
}
println()

//普通写法
val file = File("E:\\test.txt")
file.setExecutable(true)
file.setReadable(true)
println(file.readLines())
println()
//apply写法
file.apply {
setExecutable(true)
}.apply {
setReadable(true)
}.apply {
println(readLines())
}
}

apply: .apply
1.apply函数返回类型由调用对象本身决定
2.apply函数的匿名函数里面持有的是this == 调用对象本身

Kotlin语言的let内置函数

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
fun main() {
//普通方式 对集合第一个元素相加
val list = listOf(6, 5, 2, 3, 5, 7)
val value1 = list.first()
val result1 = value1 + value1
println(result1)

//let方式 对集合第一个元素相加
val result2 = listOf(6, 5, 2, 3, 5, 7).let {
//it == list 集合
it.first() + it.first()//匿名函数最后一行作为返回值 apply永远返回调用对象本身
}
println(result2)
println(getMethod1(null))
println(getMethod2("zhangSan"))
println(getMethod3(null))
println(getMethod4("zhangSan"))
}

//普通函数判断null 并返回
fun getMethod1(value: String?): String {
return if (value == null) "你传递的内容为null" else "欢迎${value}登录"
}

//简写
fun getMethod2(value: String?) = if (value == null) "你传递的内容为null" else "欢迎${value}登录"

//let 方法+ 空合并操作符判断null 并返回
fun getMethod3(value: String?): String {
return value?.let {
"欢迎${value}登录"
} ?: "你传递的内容为null"
}

//简写
fun getMethod4(value: String?) = value?.let {
"欢迎${value}登录"
} ?: "你传递的内容为null"

let: .let
1.let函数返回类型是根据匿名函数最后一行变化而变化
2.let函数的匿名函数里面持有的是it == 调用对象本身

Kotlin语言的run内置函数

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
fun main() {
//run函数特点 字符串延时
val str = "My name is zhangSan"
val r1: Float = str.run {
//this == str
5435.5f
}
println(r1)
//具名函数配合run函数
//具名函数判断长度 isLong
str.run {

}
//具名函数
str
.run(::isLong)
.run(::showText)
.run(::mapText)
.run(::println)

//let写法
str
.let(::isLong)
.let(::showText)
.let(::mapText)
.let(::println)
//匿名函数
str.run {
length > 5
}.run {
if (this) "字符串长度大于5" else "字符串长度不大于5"
}.run {
"[$this]"
}.run {
println(this)
}
}

fun isLong(str: String) = str.length > 5
fun showText(isLong: Boolean) = if (isLong) "字符串长度大于5" else "字符串长度不大于5"
fun mapText(getShow: String) = "[$getShow]"

run: .run
1.run函数返回类型是根据匿名函数最后一行变化而变化
2.run函数的匿名函数里面持有的是this == 调用对象本身

Kotlin语言的with内置函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fun main() {
var str = "zhangSan"
//具名操作
val len = with(str, ::getStrLen)
val info = with(len, ::getLenInfo)
val map = with(info, ::getInfoMap)
with(map, ::showM)
println()
//匿名操作
with(with(with(with(str) {
length
}) {
"字符串长度为:$this"
}) {
"[$this]"
}) {
println(this)
}
}

fun getStrLen(str: String) = str.length
fun getLenInfo(len: Int) = "字符串长度为:$len"
fun getInfoMap(info: String) = "[$info]"
fun showM(content: String) = println(content)

with: with()
1.with函数返回类型是根据匿名函数最后一行变化而变化
2.with函数的匿名函数里面持有的是this == 调用对象本身

Kotlin语言的also内置函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.File

fun main(){
val str="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
//可以链式调用
str.also {
println("str:$it")
}.also {
println("str装化为小写:${it.toLowerCase()}")
}.also {
println("长度为:${it.length}")
}
val file = File("E:\\test.txt")
file.also {
it.setExecutable(true)
}.also {
it.setReadable(true)
}.also {
println(it.readLines())
}
}

also: .also
1.also函数返回类型由调用对象本身决定
2.also函数的匿名函数里面持有的是it == 调用对象本身

Kotlin语言的takeIf内置函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun main() {
println(checkPermission("zhangSan", "12345"))
//真正用途
println(checkPermission2("zhangSanSan", "12345"))
}

//name.takeIf{ true/false }
//true: 返回name
//false: 返回null
public fun checkPermission(name: String, pwd: String): String? {
return name.takeIf { permissionSystem(name, pwd) }
}

//takeIf + 空合并操作符
public fun checkPermission2(name: String, pwd: String): String {
return name.takeIf { permissionSystem(name, pwd) } ?: "error"
}

fun permissionSystem(name: String, pwd: String): Boolean {
return name == "zhangSan" && pwd == "12345"
}

name.takeIf{ true/false }

  • true: 返回name
  • false: 返回null

大多情况下都是 takeIf + 空合并操作符 一起使用

Kotlin语言的takeUnless内置函数

与takeIf功能是相反的

name.takeUnless{ true/false }

  • true: 返回null
  • false: 返回name
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Manager {
private var infoValue: String? = null
fun getInfoValue() = infoValue
fun setInfoValue(infoValue: String) {
this.infoValue = infoValue
}
}

fun main() {
val manager = Manager()
manager.setInfoValue("zhangSan")
//小结:takeUnless + it.isNullOrBlank()一起使用可以验证字符串有没有初始化
val r = manager.getInfoValue().takeUnless { it.isNullOrBlank() } ?: "未经过任何初始化"
println(r)
}

List创建与元素获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun main() {
val list = listOf("zhangSan", "liSi", "wangWu", "zhaoLiu")
//普通取值 索引 运算符重载
println(list[0])
println(list[1])
println(list[2])
println(list[3])

//防止崩溃取值方式 getOrElse getOrNull
println(list.getOrElse(3) { "越界" })
println(list.getOrElse(5) { "越界" })
println()
println(list.getOrNull(3))
println(list.getOrNull(5))
//一般 getOrNull+空合并操作符
println(list.getOrNull(5) ?: "越界")
}

可变List集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val list = mutableListOf("zhangSan", "liSi", "wangWu")
list.add("zhaoLiu")
list.remove("wangWu")
println(list)
//可变集合 to 不可变集合
val list1:List<String> = list.toList()
println(list1)

//不可变集合 to 可变集合
val list2 = listOf(123, 456, 789)
val list3: MutableList<Int> = list2.toMutableList()
list3.add(890)
list3.remove(123)
println(list3)
}

Kotlin语言的mutator函数

1
2
3
4
5
6
7
8
9
10
11
fun main() {
val list: MutableList<String> = mutableListOf("zhangSan", "liSi", "wangWu")
list += "张三"//mutator的特性 += -= 就是运算符的重载
list += "李四"
list -= "zhangSan"
println(list)
//removeIf
//list.removeIf{true}//true:自动遍历整个集合,进行一个一个元素的输出
list.removeIf { it.contains("li") }//过滤所有的元素 只要包含有li的元素就是true、
println(list)
}

List集合编历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun main() {
val list = listOf(1, 2, 3, 4, 5, 6, 7)
//1.
for (i in list) {
print("元素:$i ")
}
println()
//2.
list.forEach {
//it = 每一个元素
print("元素:$it ")
}
println()
//3.
list.forEachIndexed { index, item ->
print("下标:$index,元素:$item")
}
}

Kotlin语言的解构语法过滤元素

1
2
3
4
5
6
7
8
9
10
11
12
fun main() {
val list: List<String> = listOf("zhangSan", "liSi", "wangWu")
val (value1, value2, value3) = list
print(value1)
print(value2)
print(value3)
println()
//使用_内部可以不接收赋值,可以节约一点性能
val (_, n1, n2) = list//_用来过滤解构赋值,不接收赋值
println(n1)
println(n2)
}

Set创建与元素获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun main() {
val set: Set<String> = setOf("zhangSan", "liSi", "zhaoLiu", "zhangSan")//set集合不会输出重复元素
println(set)
//set[0] set集合无[]使用方法
println(set.elementAt(0))
println(set.elementAt(1))
println(set.elementAt(2))
//println(set.elementAt(3)) 崩溃 重复元素不算
println()
println(set.elementAtOrElse(0) { "越界" })
println(set.elementAtOrElse(3) { "越界" })
println()
println(set.elementAtOrNull(0))
println(set.elementAtOrNull(3))
println()
//elementAtOrNull+空合并操作符
println(set.elementAtOrNull(3) ?: "越界")
}

可变Set集合

1
2
3
4
5
6
7
8
fun main() {
val set: MutableSet<String> = mutableSetOf("zhangSan", "liSi", "zhaoLiu")
set += "张三"
set -= "zhangSan"
set.add("李四")
set.remove("liSi")
println(set)
}

Kotlin语言的集合转换与快捷函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main(){
val list :MutableList<String> = mutableListOf("zhangSan","liSi","Jack","Tom","zhangSan")
println(list)
//List 转 Set 集合 去重
val set=list.toSet()
println(set)
//List 转 Set 再转 List 集合 去重
val list2 = list.toSet().toList()
println(list2)
//快捷函数去重 distinct
println(list.distinct())
//等价写法
println(list.toMutableSet().toList())
}

Kotlin中的数组类型

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
import java.io.File

fun main() {
val intArray = intArrayOf(1, 2, 3, 4, 5)
println(intArray[0])
println(intArray[1])
println(intArray[2])
println(intArray[3])
println(intArray[4])
// println(intArray[5]) 越界
println()
//elementAtOrElse elementAtOrNull
println(intArray.elementAtOrElse(0) { -1 })
println(intArray.elementAtOrElse(5) { -1 })
println()
println(intArray.elementAtOrNull(0))
println(intArray.elementAtOrNull(5))
//elementAtOrNull+ 空合并操作符
println(intArray.elementAtOrNull(5) ?: "越界")
//List集合转数组
println()
val charArray = listOf('A', 'B', 'C', 'D').toCharArray()
println(charArray)
//arrayOf Array<File>
val objArray = arrayOf(File("AAA"), File("BBB"), File("CCC"))
}

map创建

1
2
3
4
fun main() {
val map1 = mapOf("zhangSan" to (687.90), "liSi" to 7868.90)
val map2 = mapOf(Pair("zhangSan", 5676.78), Pair("liSi", 6768.66))
}

读取map值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun main() {
val map1 = mapOf("zhangSan" to 123, "liSi" to 566)
//1.重载[]运算符
println(map1["zhangSan"])
println(map1["xxx"])//找不到返回null
//2.getOrDefault 推荐
println(map1.getOrDefault("zhangSan", -1))
println(map1.getOrDefault("xxx", -1))
//3.getOrElse 推荐
println(map1.getOrElse("zhangSan") { -1 })
println(map1.getOrElse("xxx") { -1 })
//4.get 与 [] 等价
println(map1.get("zhangSan"))
println(map1.get("xxx"))
//5.getValue 建议不要使用
println(map1.getValue("zhangSan"))
//println(map1.getValue("xxx"))//崩溃
}

遍历map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fun main() {
val map1 = mapOf("zhangSan" to 123, "liSi" to 566, Pair("Jack", 678))
//1.
map1.forEach {
println("k:${it.key}, v:${it.value}")
}
println()
//2.
map1.forEach { key, value ->//覆盖it
println("k:$key, v:$value")
}
println()
//3.
map1.forEach { (k, v) ->
println("k:$k, v:$v")
}
println()
//4.
for (item in map1) {
println("k:${item.key}, v:${item.value}")
}
}

可变Map集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
//1.可变集合操作+= [] put
val map = mutableMapOf("zhangSan" to 123, "liSi" to 566, Pair("Jack", 678))
map += "AAA" to (123)
map += "BBB" to 123
map -= "zhangSan"
map["CCC"] = 345
map.put("DDD", 678)//put 和 []等价
//2.getOrPut 如果没有key值,就添加 有就不添加
val r1 = map.getOrPut("FFF"){4654}//返回添加的value
val r2 = map.getOrPut("AAA"){4654}//返回原来key值对应的value
println(r1)
println(r2)
println(map)
}

Kotlin语言的定义类与field关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person {
var name = "Tom"
var value = "ABCD"

/*默认存在
get() = field
set(value) {
field = value
}
*/
var info = "Tom and Jerry"
get() = field.capitalize()
set(value) {
field = "**[$value]**"
}
}

fun main() {
Person().name = "Jerry"
println(Person().name)
println(Person().info)
Person().info = "liSi"
println(Person().info)
}

Kotlin语言的计算属性和防范竞态条件

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
class Person {
val number = 0//val 只有getXXX 没有setXXX

//计算属性
val number2: Int
//从1 到 1000取出一个值返回给get函数
get() = (1..1000).shuffled().first()
var info: String? = ""

//防范竞态条件 当调用成员(可能为null或者""),必须采用
fun getShowInfo(): String {
return info?.let {
if (it.isBlank()) {
"空值"
} else {
"info:$it"
}
} ?: "null"
}
}

fun main() {
println(Person().number)
println(Person().number2)
println(Person().getShowInfo())
}

Kotlin语言的主构造函数

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
class Person(_name: String, _sex: Char, _age: Int, _info: String) {
//主构造函数:规范 _xxx 临时输入类型 不能直接使用 需要接收成变量才能使用
var name = _name
get() = field//get不允许私有化
private set(value) {
field = value
}
val sex = _sex
val age = _age
get() = if (field < 0) -1 else field
val info = _info
get() = "[$field]"

fun show() {
println(name)
println(sex)
println(age)
println(info)
}
}

fun main() {
val p = Person(_name = "zhangSan", _sex = '男', _age = 78, _info = "study")
//println(p.name)
p.show()
}

Kotlin语言的主构造函数里定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person(var name: String, val sex: Char, val age: Int, val info: String) {
fun show() {
println(name)
println(sex)
println(age)
println(info)
}
}

fun main() {
val p = Person(name = "zhangSan", sex = '男', age = 78, info = "study")
//println(p.name)
p.show()
}

Kotlin次构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Example(name:String)//主构造
{
//次构造函数 必须要调用主构造函数
constructor(name:String,sex:Char):this(name){
println("name:$name,sex:$sex")
}
//次构造函数 必须要调用主构造函数
constructor(name:String,sex:Char,info:String):this(name){
println("name:$name,sex:$sex,info:$info")
}
}
fun main(){
val p = Example("zhangSan")
Example("liSi",'男')
Example("Rose",'女',"like Jack")
}

Kotlin语言的构造函数中的默认参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Example(name:String="zhangSan")//主构造
{
//次构造函数 必须要调用主构造函数
constructor(name:String="zhangLi",sex:Char='男'):this(name){
println("name:$name,sex:$sex")
}
//次构造函数 必须要调用主构造函数
constructor(name:String="zhangSui",sex:Char,info:String="like marry"):this(name){
println("name:$name,sex:$sex,info:$info")
}
}
fun main(){
Example()//优先调用主构造
}

Kotlin语言的初始化块

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
class Test(username:String,userage:Int,usersex:Char)
{
//初始化块 init代码块
init {
println("主构造函数 $username $userage $usersex")
//第一个参数false就会调用第二个参数lambda
//判断name是不是空值 isNotBlank
require(username.isNotBlank()){
"username is null"
}
require(userage>0){"userage does not true"}
require(usersex=='男'||usersex=='女'){"sex error"}
}
constructor(username: String):this(username,87,'男'){
println("次构造函数 $username")
}
}
fun main(){
Test("zhangSan", userage = 88, usersex = '男')//调用主构造
println()
Test("liSi")//调用次构造
println()
//username is null
//Test("")
//println()
//userage does not true
//Test("Jack", userage = -1, usersex = '女')
}

Kotlin语言的构造初始化顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A(_name: String, val sex: Char)//主构造
{
val mName = _name

init {
val nameValue = _name
println("init代码块 name:$nameValue")
}

constructor(name: String, sex: Char, age: Int) : this(name, sex) {
println("次构造 name:$name,sex:$sex,age:$age")
}
}

fun main() {
A("zhangSan", '男', 89)//调用次构造
}

Kotlin语言的延迟初始化lateinit

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
class A
{
//lateinit 使用时手动加载的懒加载
lateinit var responseInfo:String
//模拟服务器请求
fun load(){//延时初始化 属于懒加载 当使用时才加载
responseInfo = "加载成功"
}
fun showResponseResult(){
//println("responseInfo:$responseInfo")
if(::responseInfo.isInitialized){
println("responseInfo:$responseInfo")
}else{
println("没有初始化加载")
}
}
}

fun main() {
val r = A()
//使用时才加载
//r.load()
//使用
r.showResponseResult()
}

Kotlin语言的惰性初始化by lazy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//惰性初始化 by lazy 使用时自动加载的懒加载方式
class KtBase {
//普通方式
//val databaseData1:String=readSQLServerDatabaseAction()
//by lazy 懒加载
val databaseData2 by lazy {
readSQLServerDatabaseAction()
}
private fun readSQLServerDatabaseAction(): String {
println("loading...")
println("loading...")
println("loading...")
println("loading...")
println("loading...")
return "database load success."
}
}
fun main(){
val p = KtBase()
Thread.sleep(5000)
println("结果:${p.databaseData2}")
}

Kotlin进阶

双冒号::引用

::后面只能引用属性函数,变量是无法被引用的,引用出来的是对应的引用对象,我们可以把引用当成反射来进行编写,能清晰一点,基础已经讲解了,这里就不在赘述。

by关键字各种委托

类的委托

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun main() {
println(HomeService(HomeDaoImpl()).getAllData())
}

//模拟数据库操作
interface HomeDao {
fun getAllData(): List<String>
}

class HomeDaoImpl : HomeDao {
override fun getAllData(): List<String> = listOf("home")
}

class HomeService(homeDaoImpl: HomeDaoImpl) : HomeDao by homeDaoImpl {
fun getRedisData(): String {
return "redis"
}
}

使用by关键字进行委托:

  1. 可以降低直接实现HomeDao而导致的冗余
  2. 不需要使用类似SpringBoot的注入字段

也可以有多个委托

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
fun main() {
println(HomeService(HomeDaoImpl(),ADaoImpl()).getAllData())
println(HomeService(HomeDaoImpl(),ADaoImpl()).getById(123))
}

//模拟数据库操作
interface HomeDao {
fun getAllData(): List<String>
}

interface ADao {
fun getById(id: Int): String
}

class HomeDaoImpl : HomeDao {
override fun getAllData(): List<String> = listOf("home")
}

class ADaoImpl : ADao {
override fun getById(id: Int): String {
return id.toString()
}
}

class HomeService(homeDaoImpl: HomeDaoImpl, aDaoImpl: ADaoImpl) : HomeDao by homeDaoImpl,
ADao by aDaoImpl {
fun getRedisData(): String {
return "redis"
}
}

属性委托

属性在顶层函数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

fun main() {
var test: String by TestClass()
test = "123"
println(test)
}

//继承几乎所有元数据都会继承的get、set接口
//第一个泛型为属性所属对象的泛型 test在顶层函数里,不属于任何类,所以写Nothing,可能为空
//第二个泛型为数据的类型
class TestClass : ReadWriteProperty<Nothing?, String> {
private var myVar = ""
override fun getValue(thisRef: Nothing?, property: KProperty<*>): String = myVar

override fun setValue(thisRef: Nothing?, property: KProperty<*>, value: String) {
myVar = value
}
}

属性在类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

fun main() {
A().apply {
aTest = "hello"
println(aTest)
}
}

class A {
var aTest: String by TestClass()
}

class TestClass : ReadWriteProperty<A, String> {
private var myVar = ""
override fun getValue(thisRef: A, property: KProperty<*>): String = myVar

override fun setValue(thisRef: A, property: KProperty<*>, value: String) {
myVar = value
}
}

延迟委托by lazy

查看基础部分

observable监听委托

可以用来监听值的改变,类似于vue中的watch

1
2
3
4
5
6
fun main() {
var test by Delegates.observable("hello") { _, oldVal, newVal ->
println("oldVal:$oldVal newVal:$newVal")
}
test = "world"
}

自己实现一个简易监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

fun main() {
var test by TestClass("hello") { oldVal, newVal ->
println("oldVal:$oldVal newVal:$newVal")
}
test = "world"
}

class TestClass(private var defaultVal: String = "", val onChange: (String, String) -> Unit) :
ReadWriteProperty<Nothing?, String> {
override fun getValue(thisRef: Nothing?, property: KProperty<*>): String = defaultVal

override fun setValue(thisRef: Nothing?, property: KProperty<*>, value: String) {
if (defaultVal == value) return
onChange(defaultVal, value)
defaultVal = 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
26
27
28
29
fun main() {
A(1).test(2)
1.b(2)
}

//T:Number 约束T为Number及其子类
class A<T:Number>(override val param: T) : B<T>(), C<T> {
override fun test(data: T) {
println(data)
}
}

abstract class B<T> {
abstract fun test(data: T)
}

interface C<T> {
val param: T
}

//顶层函数
fun <T> a(data: T) {

}
//也可以写扩展函数
fun <T> T.b(data: T) {
println(data)
println(this)
}

泛型擦除:

Java 泛型的类型擦除是指在编译时将所有泛型类型参数擦除,转换为 Object 类型,然后在运行时无法获得泛型类型的信息。类型擦除的主要目的是避免过多的创建类而造成的运行时的过度消耗。

kotlin中也存在,但kotlin可以将函数声明为 inline 函数,并使用 reified 关键字保存一下。但声明为 inline 函数将增加编译后的代码量。

1
2
3
4
5
6
7
8
9
10
fun main() {
A(1).test(2)
}

class A<T>(param:T){
inline fun <reified T>test(data:T){
val java = T::class.java //可以直接使用T,获取反射信息
println(java.simpleName)
}
}

out和in关键字

在泛型前面加out关键字,泛型就具有了协变的特性,你可以从一个子类转换为一个父类,不能用于函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fun main() {
val dog: AnimalManager<Dog> = object : AnimalManager<Dog> {
override fun getAnimal(): Dog {
return Dog()
}
}
handlerAnimal(dog)
}

abstract class Animal
class Dog : Animal() {
val name = "dog"
}

interface AnimalManager<out T> {
fun getAnimal(): T
}

//正常情况下是不能将AnimalManager<Dog>赋值给AnimalManager<Animal>
fun handlerAnimal(animalManager: AnimalManager<Animal>) {
val dog: Dog = animalManager.getAnimal() as Dog
println(dog.name)
}

在泛型前面加in关键字,泛型就具有了逆变的特性

1
2
3
4
5
6
7
8
9
interface Read<out T> {
// fun getItem(data:T) 报错:只能用在返回类型上
fun getItem():T
}

interface Write<in T> {
// fun getItem():T 报错:只能用在参数类型上
fun setItem(data: T)
}

*通配符

*==Any?

1
2
3
4
5
6
fun main() {
val list: List<*> = listOf("1213", 123, null)
list.forEach {
println(it is Int)
}
}

where

给泛型添加约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
MyClass<TestClass>()
test<TestClass>()
}

interface A
interface B
interface C
class TestClass : A, B, C
class MyClass<T> where T : A, T : B, T : C

fun <T> test() where T : A, T : B, T : C {

}

Kotlin 特性

kotlin1.5

无限持续时间值Duration

表示一个时刻与另一个瞬间相距的时间量。当第二个瞬间早于第一个瞬间时,可能会出现负持续时间。该类型可以存储持续时间值,精度可达 ±146 年,精度可达纳秒,毫秒精度可达 ±1.46 亿年。如果在 kotlin 中提供了持续时间返回操作。time 产生的持续时间值不适合上述范围,返回的 Duration 是无限的。

INFINITE 可用于表示无限超时。若要构造持续时间,请使用扩展函数 toDuration 或扩展属性 hours、minutes、seconds 等,这些属性可用于 Int、Long 和 Double 数值类型。若要获取以特定持续时间单位表示的此持续时间的值,请使用函数 toInt、toLong 和 toDouble 或属性 inWholeHours、inWholeMinutes、inWholeSeconds、inWholeNanoseconds 等。

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
// 自己实现measureTime
inline fun timeCost(block: () -> Unit): Long {
val startTime = System.currentTimeMillis()
block()
return System.currentTimeMillis() - startTime
}

// 自己实现measureTimedValue
inline fun <T> timeCostValue(block: () -> T): Pair<T, Long> {
val startTime = System.currentTimeMillis()
val result = block()
return result to System.currentTimeMillis() - startTime
}


suspend fun main() {
val duration = measureTime {
Thread.sleep(100)
}
println("measureTime ===> duration: $duration")

val timeCost = timeCost {
Thread.sleep(100)
}
println("timeCost ===> timeCost: $timeCost")

val (value, measuredTime) = measureTimedValue {
Thread.sleep(100)
100
}
println("measureTimedValue ===> value: $value, measuredTime: $measuredTime")


val (value2, measuredTime2) = timeCostValue {
Thread.sleep(100)
100
}
println("timeCostValue ===> value: $value2, measuredTime: $measuredTime2")

val scope = CoroutineScope(Dispatchers.IO)
scope.launch {
val time = measureTime {
delay(1000)
}
println(time)

//上面的代码等价于下面的代码 下面代码其实是 measureTime 的内部实现
val mark = TimeSource.Monotonic.markNow()
delay(1000)
println(mark.elapsedNow())
}.join()
}

JvmRecord注解

为了迎合java的record类,同时@JvmRecord class属性只能是val,不能是var,因为java中的record数据是不可变的,同时record中只能定义静态变量

1
2
3
4
5
6
7
8
9
10
11
12
@JvmRecord
data class KotlinPerson(val name: String, val age: Int)

//不使用@JvmRecord注解
data class PersonNotRecord(val name: String, val age: Int)

fun main() {
val javaPerson = JavaRecordUse.JavaPerson("yuanyuan", 18)
val kotlinPerson = KotlinPerson("yuanyuan", 18)
println(javaPerson)
println(kotlinPerson)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class JavaRecordUse {

public record JavaPerson(String name, int age) {
public JavaPerson {
if (name == null) throw new IllegalArgumentException("name cannot be null");
if (age < 0 || age > 100) throw new IllegalArgumentException("age must between 0 and 100");
}
}

public static void main(String[] args) {
var javaPerson = new JavaPerson("yuanyuan", 18);
System.out.println("javaPerson.name = " + javaPerson.name() + ", javaPerson.age = " + javaPerson.age());
var kotlinPerson = new KotlinPerson("yuanyuan", 18);
System.out.println("kotlinPerson.name = " + kotlinPerson.name() + ", kotlinPerson.age = " + kotlinPerson.age());
var personNotRecord = new PersonNotRecord("yuanyuan", 18);
System.out.println("personNotRecord.name = " + personNotRecord.getName() + ", personNotRecord.age = "
+ personNotRecord.getAge());
}
}

value class

用来定义基本类型那样的数值,取代inline class,只能定义一个参数

image-20240802190427595
1
2
3
4
inline class DurationForLegacy(val value: Long)

@JvmInline
value class DurationForNew(val value: Long)

kotlin1.6

Exhaustive when

使用 when 时不要漏掉任何分支,1.6只是警告,1.7直接报错

suspend父类、挂起转换

使用suspend作为父类时注意:

  1. 不能在超类型列表中混合使用普通函数类型和挂起函数类型。
  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
27
28
29
30
31
32
33
34
35
//suspend父类
class MyClickAction0 : () -> Unit {
override fun invoke() {
TODO()
}

}

class MyClickAction1 : suspend () -> Unit {
override suspend fun invoke() {
TODO()
}
}

fun launchOnClick(action: suspend () -> Unit) {}

//挂起转换
fun getSuspending(suspending: suspend () -> Unit) {}

fun suspending() {}

fun test(regular: () -> Unit) {
getSuspending { } // OK
getSuspending(::suspending) // 1.4 OK
getSuspending(regular) // 1.6 OK
val x: () -> Unit = {}
val y: suspend () -> Unit = {}
}

fun main() {
launchOnClick(MyClickAction0())
launchOnClick(MyClickAction1())
//val clickAction0:suspend ()->Unit = MyClickAction0() //报错 没有继承关系
val clickAction1: suspend () -> Unit = MyClickAction1()
}

注解实例化与Repeatable

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
annotation class X

// 对类的类型参数上注解的支持 这样反射和注解处理器都可以访问
@Target(AnnotationTarget.TYPE_PARAMETER)
annotation class BoxContent

class Box<@BoxContent T> {}

// 1.8 JVM 目标平台中运行时保留的可重复注解
@Repeatable
annotation class Tag(val value: String)

annotation class TagContainer(val value: Array<JvmTag>)

@JvmRepeatable(TagContainer::class) // java中的@Repeatable
annotation class JvmTag(val value: String)


@Tag("hello")
@Tag("world")
class Abc

fun main() {
val x = X()
}

递归泛型推导增强

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
// 递归泛型
abstract class AbsBuilder<B : AbsBuilder<B>> {
val self: B = this as B // 获取子类类型
private var id: Int? = null

fun setId(id: Int): B {
this.id = id
return self
}
}

open class MyBuilder1 : AbsBuilder<MyBuilder1>() {
private var name: String? = null

fun setName(name: String): MyBuilder1 {
this.name = name
return this
}
}

class MyBuilder2 : MyBuilder1() {
private var age: Int? = null

fun setAge(age: Int): MyBuilder2 {
this.age = age
return this
}
}

fun main() {
MyBuilder1().setId(1).setName("yuanyuan") // error < 1.6
// MyBuilder2().setId(2).setName("yuanyuan").setAge(18) // 报错 需要强转
(MyBuilder2().setId(2).setName("yuanyuan") as MyBuilder2).setAge(22)
}

此时需要修改MyBuilder1:

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
// 递归泛型
abstract class AbsBuilder<B : AbsBuilder<B>> {
val self: B = this as B // 获取子类类型
private var id: Int? = null

fun setId(id: Int): B {
this.id = id
return self
}
}

open class MyBuilder1<B : MyBuilder1<B>> : AbsBuilder<B>() {
private var name: String? = null

fun setName(name: String): B {
this.name = name
return self
}
}

class MyBuilder2 : MyBuilder1<MyBuilder2>() { // MyBuilder2没有子类 直接将MyBuilder2传入
private var age: Int? = null

fun setAge(age: Int): MyBuilder2 {
this.age = age
return this
}
}

fun main() {
MyBuilder1().setId(1).setName("yuanyuan")
MyBuilder2().setId(2).setName("yuanyuan").setAge(22)
//用处:解决log4j WriterAppender.newBuilder<B>() B的传参
}

Builder Inference

延迟的类型推导

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
import kotlin.experimental.ExperimentalTypeInference

class Container<T> {
fun getValue(): T = TODO()
fun setValue(value: T) {

}
}

@OptIn(ExperimentalTypeInference::class)
fun <T> buildContainer(@BuilderInference builder: Container<T>.() -> Unit) {

}

fun main() {
buildContainer {
setValue(10)
getValue()
}

buildList {
add("hello")
add("world")
set(1, "kotlin")
get(0)
}

buildMap {
put("hello", 1)
}

buildSet {
add(100)
}

sequence {
yield(100)
}
}

Context Receiver

开启Context Receiver

build.gradle.kts

1
2
3
4
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
freeCompilerArgs = listOf("-Xcontext-receivers")
}

在logger中的使用

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
import com.google.gson.Gson
import kotlinx.coroutines.flow.asFlow

interface Logger {
fun log(message: Any?)
}

object StdOutLogger : Logger {
override fun log(message: Any?) = println(message)
}

object StdErrLogger : Logger {
override fun log(message: Any?) = System.err.println(message)
}

object JsonLogger : Logger {
override fun log(message: Any?) = println(Gson().toJson(message))
}

fun <E> List<E>.loggerOnEach(logger: Logger): List<E> {
forEach { logger.log(it) }
return this
}

context(Logger)
fun <E> List<E>.loggerOnEach(): List<E> {
forEach { log(it) }
return this
}

data class User(val id: Int, val name: String)

fun main() {
listOf(1, 2, 3).loggerOnEach(StdOutLogger).asFlow()
listOf("1", "2", "3").loggerOnEach(StdOutLogger)
"Hello".toList().loggerOnEach(StdOutLogger)

with(StdErrLogger) {
listOf(1, 2, 3).loggerOnEach()
listOf("1", "2", "3").loggerOnEach()
"Hello".toList().loggerOnEach()
}

with(JsonLogger) {
listOf(User(1, "yuanyuan"), User(2, "kotlin")).loggerOnEach()
}
}

dispatch receiver:如果一个类有一个函数,那这个类就是这个函数的 dispatch receiver

image-20240802233700241

extension receiver:定义扩展函数时,对于扩展函数来讲,扩展的类型就是他的 extension receiver

image-20240802233628849