Android Kotlin语法
- Kotlin与JVM
- Kotlin 语言的跨平台特性
- Kotlin语法
- 面试题
- Kotlin与Java的主要区别是什么?
- 协程(方便的线程并发框架)
- 空安全(特性)
- Object / companion Object / top-level
- const
- Kotlin中reified修饰符有什么用?inline
- 函数式编程
- 解释Kotlin中的let, apply, run, with和also函数的区别
- Kotlin中的委托属性(property delegation)如何工作?
- 22. Kotlin 中的密封类是什么?
- 22. Kotlin 的 by接口委托
- Kotlin的扩展函数
- 密封类 + 密封接口
- 对象表达式
Kotlin与JVM

Kotlin 语言的跨平台特性

Kotlin语法
1、快速了解Kotlin
Hello Kotlin
fun main() {
println("Hello world") // 输出:Hello world
//声明变量
var hello : String = "Hello"
var world = "world"
//以及字符串拼接
println("$hello $world") // 输出:Hello world
//内置数据类型
/*
String 字符串
Char 单字符
Boolean true/false
Int 整型
Double 小数
List 集合
Set 无重复集合
Map 键值对集合
*/
}
var 与 val
val 只读变量
var 可写可读的变量
fun main() {
// val 只读不能修改
val info : String = "info"
// info = "new info" --> 这段代码会报错
// var 可以修改
var info2 : Int = 1
println(info2) // 输出 1
info2 = 2
println(info2) // 输出 2
}
类型推导
Kotlin会类型推导
根据赋值时的类型,给变量定义对应的类型
fun main() {
// 根据赋值时的类型,给变量定义对应的类型
val info : String = "info"
// ": Stirng" 是可以不用写的
val info2 = "info"
// 同理
val age : Int = 10
val age2 = 10
// 同理
val flag : Boolean = false
val flag2 = false
}
编译时常量 const
// const 是编译时常量
// 编译时常量只能在函数之外定义,编译期间初始化
const val A = 10
fun main() {
// const 用于定义常量
// const val A = 10 报错提示:const不适用于局部变量
val info = A // val是只读变量
println(info) // 输出 10
}
查看Kotlin反编译后字节码
编译器的 Tool - Kotlin - Show Kotlin BtyeCode可以查看Kotlin的字节码。

因为Kotlin要运行在JVM上所以它的字节码就是Java的字节码。
Java字节码转Java代码

引用类型
Kotlin的引用类型,都是Kotlin实现的类型
如: Int \ Float \ Double \ Boolean
它们最终都会转换成java的基础类型。

转化成java后

rane表达式
fun main() {
val number = 100
if(number in 10..50){
println("在 10 到 50 以内,闭包的")
}else if(number in 100..1000){
println("在 100 到 1000 以内,闭包的")
}else if(number in 0..100){
println("在 0 到 100以内,闭包的")
}
// 输出结果: 在 100 到 1000 以内,闭包的
}
when表达式
在 Kotlin 中 if 和 when 都是表达式,是有返回值的
fun main() {
var week = 5
// 在 Kotlin 中 if 和 when 都是表达式,是有返回值的
var info = when(week){
1 -> "1111111"
2 -> "2222222"
3 -> "3333333"
4 -> "4444444"
5 -> "5555555"
else -> {
println("other")
}
}
println("info : $info") // info : 5555555
week = 7
info = when(week){
1 -> "1111111"
2 -> "2222222"
3 -> "3333333"
4 -> "4444444"
5 -> "5555555"
else -> {
"other"
}
}
println("info : $info") // info : other
}
String模板
fun main() {
val name = "name"
val count = 10
println("name:$namecount:$count") // 报错!!,因为她会识别“namecount”为一起的,但是又没有这个变量
println("name:$name count:$count") // 加个空格隔开就不会了
println("name:${name}count:$count") //如果就是需要贴在一起的,可以将变量包裹在{}里
val flag = true
// 还可以用when或者if填充在里面,因为Kotlin里面if和when属于表达式,而java是语句
// 也就是说Kotlin的整个if语句或when语句可以当作为一个方法函数
println("name? ${if(flag)"true" else "false"}")
}
函数
/*
修饰符 函数关键字 函数名(参数名:参数类型) :返回类型
*/
private fun method01(age:Int, name:String) : Int{
println("age $age namne $name")
return 100
}
函数默认参数
fun main() {
method01(20,"Kotlin") // 输出 age 20 namne Kotlin
// 不填写参数,会使用默认参数
method01() // 输出 age 5 namne NON
}
/*
函数的参数可以设置默认参数。
*/
private fun method01(age:Int = 5, name:String = "NON") : Int{
println("age $age namne $name")
return 100
}
具名参数函数参数
fun main() {
//具名参数,可以随意调整参数的位置
method01(name="Name2",age=10) // 输出 age 10 namne Name2
//可以单独赋值其中某些参数
method01(name="Name3") // 输出 age 5 namne Name3
}
private fun method01(age:Int = 5, name:String = "NON") : Int{
println("age $age namne $name")
return 100
}
Unit函数特点
// :Unit 可以不用写,相当于Java的void无返回
private fun method01(age:Int = 5, name:String = "NON") :Unit {
println("age $age namne $name")
return 100 //这句话会报错,因为定义函数无返回
}
private fun method02(age:Int = 5, name:String = "NON") {
println("age $age namne $name")
}
Nothing类型特点
fun main() {
method01(-1)
// 运行报错
// Exception in thread "main" kotlin.NotImplementedError: An operation is not implemented: 异常分数
// at MainKt.method01(Main.kt:8)
// at MainKt.main(Main.kt:2)
// at MainKt.main(Main.kt)
// 因为TODO是一个会抛异常的代码,于Java的注解不一样
}
private fun method01(number: Int) {
when(number){
-1 -> TODO("异常分数")
in 0..50 -> println("不及格")
in 51..70 -> println("及格")
in 71..100 -> println("优秀")
}
}
Kotlin里面 TODO 的源码
/**
* Always throws [NotImplementedError] stating that operation is not implemented.
*
* @param reason a string explaining why the implementation is missing.
*/
@kotlin.internal.InlineOnly
public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An operation is not implemented: $reason")
反引号
fun main() {
//中文也能定义
测试方法_用于单元测试_编码人Pun()
`is`(10)
}
private fun `测试方法_用于单元测试_编码人Pun`(){
println("单元测试")
}
// is 不能作为函数名称,因为is是Kotlin的关键字
// 如果需要 is 的函数,可以加``
private fun `is`(count: Int){
println("count $count")
}
2、Kotlin的函数
匿名函数
fun main() {
val len = "name-hello".count()
println(len) // 输出结果 : 10
// 匿名函数
// 以下这部分就是匿名函数部分,
// {
// it == 'l'
// }
val len2 = "name-hello".count{
it == 'l'
}
println(len2) // 输出结果 : 2
}
为什么{it==‘l’}能让count函数只返回符合条件的字符数量呢?让我们看看count的源码
// 第一个函数,就是上面输出结果为 10所调用的方法。
@kotlin.internal.InlineOnly
public inline fun CharSequence.count(): Int {
return length
}
/**
* Returns the number of characters matching the given [predicate].
*/
// 第二个函数,就是比较特别的函数
// 1.它的参数是一个函数,而不是对象或值
// 参数是一个函数,是Char作为参数Boolean值作为返回值的函数,就如同
// {
// it == 'l'
// }
public inline fun CharSequence.count(predicate: (Char) -> Boolean): Int {
var count = 0
// 遍历判断每个字符,匹配就+1,就是在计算我们指定字符的数量
for (element in this) if (predicate(element)) ++count
return count
}
函数类型于隐式返回
fun main() {
// 第一步,函数输入输出的声明
val methodAction : () -> String
// 第二步:对上面函数的实现
methodAction = {
val i = 100
// 它会以最后一行作为返回值
"String $i" // 相当于 return "String " + i
}
// 第三步:调用函数
println(methodAction()) // 输出结果 : String 100
}
函数参数
fun main() {
// 第一步,函数输入输出的声明 // 第二步:对声明面函数的实现
// 变量类型 函数名 : (参数类型) -> 返回值 = { 参数名 ->
// 函数体
// {
val methodAction : (Int,Int) -> String = { i1, i2 ->
"String $i1 $i2"
}
println(methodAction(11,22)) // 输出 : String 11 22
}
it关键字
fun main() {
// 单个参数函数会自带一个it作为参数
val method : (String) -> String = {
"String : $it"
}
println(method("443"))
// 多个参数需要自己定义参数名
// 以下代码会报错,因为没有写变量名,没有it参数
val method2 : (String,Int) -> String = {
"String : $it"
}
}
函数的类型推断
fun main() {
// 函数返回值类型的自动判断
val obj = { number : Int ->
when(number){
1 -> "11111"
2 -> 3.14f
3 -> 100
4 -> false
5 -> MyClass()
else -> Unit
}
}
println(obj(1)) // 输出 : 11111
println(obj(2)) // 输出 : 3.14
println(obj(3)) // 输出 : 100
println(obj(4)) // 输出 : false
println(obj(5)) // 输出 : MyClass@7f690630
println(obj(100)) // 输出 : kotlin.Unit
}
class MyClass{
}
函数中定义参数是函数的函数
类似于java的接口作为参数
fun main() {
//将函数作为参数
//一、匿名函数作为参数,三种写法
loginApi("nono","1234") { msg: String,code: Int ->
println("msg: $msg code: $code")
}
//输出 msg: 登录失败 code: 200
loginApi("nono","1234",{ msg: String,code: Int ->
println("msg: $msg code: $code")
})
//输出 msg: 登录失败 code: 200
loginApi("nono","1234",response = { msg: String,code: Int ->
println("msg: $msg code: $code")
})
//输出 msg: 登录失败 code: 200
//二、函数变量作为参数
val response : (String, Int) -> Unit = { msg, code ->
println("response --> msg: $msg code: $code")
}
loginApi("name","1234",response)
//输出 response --> msg: 登录成功 code: 200
}
const val USER_NAME = "name"
const val USER_PWD = "1234"
fun loginApi(username: String,pwd: String, response: (String, Int) -> Unit){
if (username == null || pwd == null){
TODO("用户名或密码 null")
}
if (username == USER_NAME && pwd == USER_PWD){
response("登录成功",200)
}else{
response("登录失败",200)
}
}
函数内联
1、为什么使用内联
我们先有如下代码,没有使用内联:
fun main() {
loginApi("nono","1234",response = { msg: String,code: Int ->
println("msg: $msg code: $code")
})
//输出 msg: 登录失败 code: 200
}
const val USER_NAME = "name"
const val USER_PWD = "1234"
fun loginApi(username: String,pwd: String, response: (String, Int) -> Unit){
if (username == null || pwd == null){
TODO("用户名或密码 null")
}
if (username == USER_NAME && pwd == USER_PWD){
response("登录成功",200)
}else{
response("登录失败",200)
}
}
将这块代码转换为java,留意代码中的注解
import kotlin.Metadata;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
@Metadata(
mv = {1, 5, 1},
k = 2,
d1 = {"\u0000\u001e\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0002\b\u0003\n\u0002\u0018\u0002\n\u0002\u0010\b\n\u0002\b\u0002\u001a0\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u00012\u0006\u0010\u0006\u001a\u00020\u00012\u0018\u0010\u0007\u001a\u0014\u0012\u0004\u0012\u00020\u0001\u0012\u0004\u0012\u00020\t\u0012\u0004\u0012\u00020\u00040\b\u001a\u0006\u0010\n\u001a\u00020\u0004\"\u000e\u0010\u0000\u001a\u00020\u0001X\u0086T¢\u0006\u0002\n\u0000\"\u000e\u0010\u0002\u001a\u00020\u0001X\u0086T¢\u0006\u0002\n\u0000¨\u0006\u000b"},
d2 = {"USER_NAME", "", "USER_PWD", "loginApi", "", "username", "pwd", "response", "Lkotlin/Function2;", "", "main", "My_Kotlin_Application.app"}
)
public final class MainKt {
@NotNull
public static final String USER_NAME = "name";
@NotNull
public static final String USER_PWD = "1234";
public static final void main() {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// 方法调用时,最后的lambda表达式会变成 一个 null.INSTANCE
// 1、这里的null并不是问题所在,这个null猜测时因为解析不出来导致的
// 2、这里的null是一个对象,然后INSTANCE又是一个对象内部属性对象
// 3、当这个方法多次调用的时候,就会产生很多对象,占用资源
loginApi("nono", "1234", (Function2)null.INSTANCE);
loginApi("nono", "1234", (Function2)null.INSTANCE);
loginApi("nono", "1234", (Function2)null.INSTANCE);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
public static final void loginApi(@NotNull String username, @NotNull String pwd, @NotNull Function2 response) {
Intrinsics.checkNotNullParameter(username, "username");
Intrinsics.checkNotNullParameter(pwd, "pwd");
Intrinsics.checkNotNullParameter(response, "response");
if (Intrinsics.areEqual(username, "name") && Intrinsics.areEqual(pwd, "1234")) {
response.invoke("登录成功", 200);
} else {
response.invoke("登录失败", 200);
}
}
}
以上java因为lambda表达式导致资源浪费问题
2、如何解决
在函数前添加 inline 关键字,启用内链
// 在函数强添加 inline 关键字
inline fun loginApi(username: String,pwd: String, response: (String, Int) -> Unit){
if (username == null || pwd == null){
TODO("用户名或密码 null")
}
if (username == USER_NAME && pwd == USER_PWD){
response("登录成功",200)
}else{
response("登录失败",200)
}
}
转换成java代码,会发现整个函数调用代码都不见了。
因为这个关键字实现了宏替换,将调用该函数的地方直接用函数的代码替换掉了
import kotlin.Metadata;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
@Metadata(
mv = {1, 5, 1},
k = 2,
d1 = {"\u0000\u001e\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0002\b\u0003\n\u0002\u0018\u0002\n\u0002\u0010\b\n\u0002\b\u0002\u001a6\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u00012\u0006\u0010\u0006\u001a\u00020\u00012\u0018\u0010\u0007\u001a\u0014\u0012\u0004\u0012\u00020\u0001\u0012\u0004\u0012\u00020\t\u0012\u0004\u0012\u00020\u00040\bH\u0086\bø\u0001\u0000\u001a\u0006\u0010\n\u001a\u00020\u0004\"\u000e\u0010\u0000\u001a\u00020\u0001X\u0086T¢\u0006\u0002\n\u0000\"\u000e\u0010\u0002\u001a\u00020\u0001X\u0086T¢\u0006\u0002\n\u0000\u0082\u0002\u0007\n\u0005\b\u009920\u0001¨\u0006\u000b"},
d2 = {"USER_NAME", "", "USER_PWD", "loginApi", "", "username", "pwd", "response", "Lkotlin/Function2;", "", "main", "My_Kotlin_Application.app"}
)
public final class MainKt {
@NotNull
public static final String USER_NAME = "name";
@NotNull
public static final String USER_PWD = "1234";
public static final void main() {
String username$iv = "nono";
String pwd$iv = "1234";
int $i$f$loginApi = false;
short code;
String msg;
boolean var5;
String var6;
boolean var7;
if (Intrinsics.areEqual(username$iv, "name") && Intrinsics.areEqual(pwd$iv, "1234")) {
code = 200;
msg = "登录成功";
var5 = false;
var6 = "msg: " + msg + " code: " + code;
var7 = false;
System.out.println(var6);
} else {
code = 200;
msg = "登录失败";
var5 = false;
var6 = "msg: " + msg + " code: " + code;
var7 = false;
System.out.println(var6);
}
username$iv = "nono";
pwd$iv = "1234";
$i$f$loginApi = false;
if (Intrinsics.areEqual(username$iv, "name") && Intrinsics.areEqual(pwd$iv, "1234")) {
code = 200;
msg = "登录成功";
var5 = false;
var6 = "msg: " + msg + " code: " + code;
var7 = false;
System.out.println(var6);
} else {
code = 200;
msg = "登录失败";
var5 = false;
var6 = "msg: " + msg + " code: " + code;
var7 = false;
System.out.println(var6);
}
username$iv = "nono";
pwd$iv = "1234";
$i$f$loginApi = false;
if (Intrinsics.areEqual(username$iv, "name") && Intrinsics.areEqual(pwd$iv, "1234")) {
code = 200;
msg = "登录成功";
var5 = false;
var6 = "msg: " + msg + " code: " + code;
var7 = false;
System.out.println(var6);
} else {
code = 200;
msg = "登录失败";
var5 = false;
var6 = "msg: " + msg + " code: " + code;
var7 = false;
System.out.println(var6);
}
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
public static final void loginApi(@NotNull String username, @NotNull String pwd, @NotNull Function2 response) {
int $i$f$loginApi = 0;
Intrinsics.checkNotNullParameter(username, "username");
Intrinsics.checkNotNullParameter(pwd, "pwd");
Intrinsics.checkNotNullParameter(response, "response");
if (Intrinsics.areEqual(username, "name") && Intrinsics.areEqual(pwd, "1234")) {
response.invoke("登录成功", 200);
} else {
response.invoke("登录失败", 200);
}
}
}
函数引用
fun main() {
// 普通的使用方式
loginApi("nono","1234",response = { msg: String,code: Int ->
println("msg: $msg code: $code")
})
//输出 msg: 登录失败 code: 200
// 函数引用
loginApi("name","1234", ::myResponse)
//输出 msg: 登录成功 code: 200
val obj = ::myResponse
loginApi("name","1234", obj)
//输出 msg: 登录成功 code: 200
}
fun myResponse(msg: String, code: Int){
println("msg: $msg code: $code")
}
const val USER_NAME = "name"
const val USER_PWD = "1234"
inline fun loginApi(username: String,pwd: String, response: (String, Int) -> Unit){
if (username == null || pwd == null){
TODO("用户名或密码 null")
}
if (username == USER_NAME && pwd == USER_PWD){
response("登录成功",200)
}else{
response("登录失败",200)
}
}
函数类型作为返回类型
fun main() {
val show = show("show")
// 输出 show : show
val show2 = show2("show2")
// 输出 show2 : show2
println(show2("name2",200))
// 输出 name : name2, code : 200
}
// 普通函数,返回String类型
fun show(info: String) : Boolean{
println(" show : $info")
return true
}
// 函数,返回函数类型
fun show2(info: String) : (String, Int) -> String{
println(" show2 : $info")
return { name: String, code: Int ->
"name : $name, code : $code"
}
}
匿名函数于具名函数
fun main() {
// 匿名函数
show("info1"){
println("it : $it")
}
// 输出 it : info1
// 具名函数
show("info2", ::myShowResult)
// 输出 myShowResult : info2
}
fun myShowResult(info: String) : Unit{
println("myShowResult : $info")
}
fun show(info: String, showResult:(String) -> Unit){
showResult(info)
}
3、Kotlin与null
可空性特性
fun main() {
var name : String = "name"
// name = null // 提示不能赋值为 null,报错
var name2 : String? = "name2"
name2 = null
println("name2 $name2")
// 输出 name2 null
}
安全调用符
fun main() {
var name : String? = "name"
name = null
// name.length // 报错,该变量可空类型,使用name需要有补救措施
name?.length // 添加 ? 后,如果变量是null,后面的代码不会执行
println("name : $name, size : ${name?.length}")
// 输出 name : null, size : null
name = "abc"
println("name : $name, size : ${name?.length}")
// 输出 name : abc, size : 3
}
let的安全调用
fun main() {
var name : String? = null
// 如果name为null,let逻辑不会执行
name?.let {
println("name $it, size : ${it.length}")
}
// 无输出
name = "eee"
name?.let {
println("name $it, size : ${it.length}")
}
// 输出 name eee, size : 3
}
非空断言操作符
fun main() {
var name : String? = null
name!!.length // 不管 name 是不是 null 都会执行,null就抛异常
// 输出
// Exception in thread "main" java.lang.NullPointerException
// at MainKt.main(Main.kt:4)
// at MainKt.main(Main.kt)
}
空合并操作符
fun main() {
var info : String? = ""
info = null
// 空合并操作符 对象 ?: 是null情况的操作
println(info?:"is null")
// 输出 is null
// 与 let 关键字的组合用法
var result = info?.let{
"info is not null $it"
} ?: "info is null"
println(result)
// 输出 info is not null
}
Kotlin先决条件函数
fun main() {
var info : String? = null
// checkNotNull(info)
// 输出 Exception in thread "main" java.lang.IllegalStateException: Required value was null.
// at MainKt.main(Main.kt:3)
// at MainKt.main(Main.kt)
// requireNotNull(info)
// 输出 Exception in thread "main" java.lang.IllegalArgumentException: Required value was null.
// at MainKt.main(Main.kt:8)
// at MainKt.main(Main.kt)
require(false)
// 输出 Exception in thread "main" java.lang.IllegalArgumentException: Failed requirement.
// at MainKt.main(Main.kt:13)
// at MainKt.main(Main.kt)
}
4、Kotlin 与 字符串
substring
fun main() {
val str = "hi hello"
val indexOf = str.indexOf("e")
// 输出 0 到 e 之间的内容
println(str.substring(0,indexOf))
// 输出 hi h
// 输出 0 到 e 之间的内容
println(str.substring(0 until indexOf))
// 输出 hi h
}
split
fun main() {
val str = "hi hello 你好"
val list = str.split(" ")
// 解构
val (str1,str2,str3) = list
println("str1 : $str1, str2 : $str2, str3 : $str3")
// 当然你也可以遍历
for (it in list){
println("it $it")
}
}
replace
fun main() {
val str = "hi hello 你好"
// 字符串.replace(Regex(要替换的内容,可用正则表达式)){
// 替换后的字符串
// }
val str2 = str.replace(Regex("[a-z]")){
when(it.value){
"h" -> "A"
"i" -> "B"
"l" -> "C"
else -> "?"
}
}
println("str2 : $str2")
// 输出 str2 : AB A?CC? 你好
}
== 与 ===
fun main() {
// == 值 内容的比较
// === 引用的比较
val name1 = "name"
val name2 = "name"
println(name1 == name2)
// 输出 true
println(name1 === name2)
// 输出 true
// 因为有字符常量池,相同字符串引用指向都是同一个内存空间
val name3 = StringBuffer("name").toString()
println(name1 === name3)
// 输出 false
// 因为 StringBuffer 会开辟新的内存空间,所以两个字符串内存地址不一样
println(name1 == name3)
// 输出 true
// 因为这里比较的是值,两个字符串都是 name 所以输出true
}
字符串遍历
fun main() {
val str = "abcdefghijk"
str.forEach {
println(" it - $it")
}
// 遍历 输出
// it - a
// it - b
// it - c
// it - d
// it - e
// it - f
// it - g
// it - h
// it - i
// it - j
// it - k
}
5、数据类型转换
安全转换函数
fun main() {
val number : Int = "123".toInt()
// 字符串 转 Int
println(number)
// 输出 123
// val number2 : Int? = "123.3".toInt()
// double字符串 转 Int 是无法转换的会报错,所以为了防止报错
val number2 : Int? = "123.3".toIntOrNull()
// toIntOrNull 在转换失败时,返回 null
println(number2)
// 输出 null
// double的函数如下,其他以此类推
// "123.3".toDouble()
// "123.3".toDoubleOrNull()
}
double 转 int
import kotlin.math.roundToInt
fun main() {
println(33.5.toInt()) // 取整
// 输出 33
println(33.5.roundToInt()) // 四舍五入
// 输出 34
println(33.5513454.roundToInt()) // 四舍五入
// 输出 34
val r = "%.3f".format(44.5678) // 保留3位小数,四舍五入
println(r)
// 输出 44.568
}
6、Kotlin的内置函数
Kotlin 的 apply
import kotlin.math.roundToInt
fun main() {
val info = "Hello World"
println("info length : ${info.length}")
println("info 最后的字符 : ${info[info.length-1]}")
println("info 改全小写 : ${info.toLowerCase()}")
println()
// apply
val infoNew : String = info.apply {
// 该函数内 this 等于 info
println("info2 length : ${length}")
println("info2 最后的字符 : ${this[length-1]}")
println("info2 改全小写 : ${toLowerCase()}")
// apply 始终返回被调用的 info 本身
}
println(infoNew)
// 输出 Hello World
// 一般用于链式调用
info.apply {
println("操作一 : ")
}.apply {
println("操作二 : ")
}.apply {
println("操作三 : ")
}
}
Kotlin的 let
fun main() {
val list = listOf(1,2,3,4,5)
val value1 = list.first() //第一个元素
val result1 = value1 + 1 //取第一个元素进行操作
println(result1)
// 输出 2
val result2 = listOf(1,2,3,4,5).let {
// it 等于 list
// let 会 return 最后一行的结果
it.first() + it.first()
}
println(result2)
// 输出 2
}
Kotlin的 run
与 let 和 apply 在使用上差不多
fun main() {
val str = "Hello world"
val r1 : Int = str.run{
this.length
}
println(r1)
// 输出 11
str.run {
// this == str本身
}
str.run(::isLong)
.run {
var str = "null"
str = if (this){
"Hi"
}else{
"Hao"
}
str // 最后一个值是返回值
}
.run {
println(this)
// 输出 Hi
}
}
fun isLong(str: String) : Boolean{
return str.length >= 10
}
Kotlin的 with
fun main() {
val str = "Name"
// with(参数,具名函数)
with(str, ::println)
// 输出 Name
with(run {
str + "HGW"
}, ::println)
// 输出 NameHGW
with(str.apply {
str + "Name2"
},::println)
// 输出 Name,因为apply只会返回自身
with(str,::test)
// 输出 test - Name
}
fun test(str:String){
println("test - $str")
}
Kotlin的 also
跟 apply 差不多
fun main() {
val r2 : Int = 123.also{
it + 1
}
println(r2)
// 输出 123
// 与apply一样只会返回原始的值
// 只不过它用的是 it 不是 this
}
Kotlin的 takeif
fun main() {
val test = "hello"
val str = test.takeIf {
true
}
// true 返回自身
println(str)
// hello
val str2 = test.takeIf {
false
}
// false 返回null
println(str2)
// null
}
Kotlin的 takeUnless
fun main() {
val test = "hello"
val str = test.takeUnless {
true
}
// true 返回null
println(str)
// null
val str2 = test.takeUnless {
false
}
// false 返回自身
println(str2)
// hello
}
7、Kotlin的集合
listOf 不可变集合
fun main() {
val list = listOf("000", "001", "002", "003")
println(list[3])
// 输出 003
println(list.getOrElse(2){"获取失败,数值越界了"})
// 输出 002
println(list.getOrElse(4){"获取失败,数值越界了"})
// 输出 获取失败,数值越界了
println(list.getOrNull(5)) // 没有5下标的元素
// 输出 null
println(list.getOrNull(1))
// 输出 001
println(list.getOrNull(10) ?: "越界")
// 输出 越界
}
mutableListOf 可变集合
fun main() {
val list : MutableList<String> = mutableListOf("000","001","002")
list.add("003")
println(list.toString())
// 输出 [000, 001, 002, 003]
list.add(0,"0000")
println(list.toString())
// 输出 [0000, 000, 001, 002, 003]
list.remove("002")
println(list.toString())
// 输出 [0000, 000, 001, 003]
list.removeAt(3)
println(list.toString())
// 输出 [0000, 000, 001]
}
mutator函数
import android.os.Build
import androidx.annotation.RequiresApi
@RequiresApi(Build.VERSION_CODES.N)
fun main() {
val list : MutableList<String> = mutableListOf("000","001","002")
list += "005"
println(list.toString())
// 输出 [000, 001, 002, 005]
list -= "000"
println(list.toString())
// 输出 [001, 002, 005]
list.removeIf {
it.contains("2") // 返回true就删除当前it
}
println(list.toString())
// 输出 [001, 005]
}
集合遍历
fun main() {
val list = listOf(1,2,3,4,5,6,7)
println(list)
for (i in list){
print("i - $i")
}
println()
list.forEach {
// it 就是 每一个元素
print("forEach - $it")
}
println()
list.forEachIndexed { index, i ->
print("index : $index , i : $i")
}
println()
}
解构函数
import android.os.Build
import androidx.annotation.RequiresApi
fun main() {
val list : List<String> = listOf("str01","str02","str03")
val(v1,v2,v3) = list
println("v1 : $v1 , v2 : $v2 , v3 : $v3")
// "_" 是用来过滤元素的,表示不接收第一个
val(_,n2,n3) = list
println("n2 : $n2 , n3 : $n3")
}
set 集合
fun main() {
val set : Set<String?> = setOf(null,"str","str","str","str04")
println(set)
// 输出 : [null, str, str04]
// 不会有重复元素。
// 获取元素
println(set.elementAt(0))
// 防止报错
println(set.elementAtOrElse(3){"越界了"})
// 防止获取 null
// 获取到数据为 null ,或者报错异常的时候
println(set.elementAtOrNull(0) ?: "越界1")
println(set.elementAtOrNull(22) ?: "越界2")
}
集合转换
fun main() {
val list : MutableList<String> = mutableListOf("001","001","002")
println(list)
val set = list.toSet() // 通过转换成功set去重
println(set)
val list02 = list.distinct() // 去重
// 源码:
// public fun <T> Iterable<T>.distinct(): List<T> {
// return this.toMutableSet().toList()
//}
println(list02)
}
数组
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()
// 越界返回 -1
println(intArray.elementAtOrElse(5){-1})
// 判空
println(intArray.elementAtOrNull(200) ?: -1)
// 集合 转 数组
val charArray = listOf('1','2','3').toCharArray()
println(charArray)
// arrayOf 随意存放对象的数值
val arr = arrayOf(Object(), Object(),"String")
println(arr.elementAt(0))
println(arr.elementAt(1))
println(arr.elementAt(2))
}
Map
1、创建

2、遍历




3、可变Map

8、Kotlin的类
成员变量/属性/初始化

field关键字,field === 对应属性

val修饰类型属性

计算属性

构造函数
主构造函数

次构造
class MyTest(name:String){ // 主构造函数
// 次构造函数,必须重写主构造函数
constructor(name:String, age:Int) : this(name){
println("次构造")
}
}
如果不传参数

init代码块

构造过程的执行顺序


lateinit关键字

lazy关键字

open关键字

面试题
Kotlin与Java的主要区别是什么?
空安全:Kotlin在类型系统中直接区分可空和非空类型
扩展函数:无需继承即可为类添加新功能
数据类:自动生成equals(), hashCode(), toString()等方法
更简洁的语法:如lambda表达式、字符串模板、属性等
协程:轻量级线程支持
协程(方便的线程并发框架)
优化了并发编程的代码复杂度

并行请求

消除嵌套代码

suspend关键词

非阻塞式挂起
非阻塞只是看起来而已,会切换切换线程去做耗时操作,然后回来继续执行后续的逻辑(约等于,回调CallBack)
空安全(特性)
kotlin里面全部的值都不允许为空的
lateinit
Object / companion Object / top-level
Object 类 : 里面全部都行都是静态的
companion Object:嵌套在类里面使用,里面的值全都是静态的
top-level:脱离与Class的静态变量
const
静态常量,但是支持基础类型
const val s = “s” // 常量内敛
Kotlin中reified修饰符有什么用?inline
(约等于Java泛型)
使泛型类型参数在运行时保留
必须与inline函数一起使用
inline fun <reified T> checkType(obj: Any) = obj is T
inline 内敛优化
online 局部取消内敛优化
crossline 局部加强内敛优化
函数式编程
Kotlin中的高阶函数是什么?举例说明
kotlin
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
return operation(x, y)
}
val sum = calculate(5, 3) { a, b -> a + b }
解释Kotlin中的let, apply, run, with和also函数的区别
- let: 对非空对象执行lambda,返回lambda结果
- apply: 在对象上执行lambda,返回对象本身
- run: 结合了let和apply的功能
- with: 类似run但不作为扩展函数
- also: 类似apply,但接收原始对象作为参数
Kotlin中的委托属性(property delegation)如何工作?
kotlin
class Example {
var p: String by Delegate()
}
class Delegate {
operator fun getValue(…): String { … }
operator fun setValue(…, value: String) { … }
}
22. Kotlin 中的密封类是什么?
有点像枚举
Kotlin 中的密封类是可以在其中定义一组有限的子类的类。它允许您限制继承层次结构并定义一组封闭的可能子类。
当您想要表示受限制的类型层次结构时,密封类非常有用,其中所有可能的子类都是预先已知的,并且应该在when表达式中进行详尽的处理。
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()
}
fun processResult(result: Result) {
when (result) {
is Result.Success -> {
println("Success: ${result.data}")
}
is Result.Error -> {
println("Error: ${result.message}")
}
Result.Loading -> {
println("Loading...")
}
}
}
在示例中,“Result”类是一个密封类,具有三个子类:“Success”、“Error”和“Loading”。 “processResult”函数演示了使用when表达式对所有可能的子类进行详尽的处理。
密封类提供了一种安全的方法来处理受限层次结构并启用详尽的模式匹配,使代码更加健壮且不易出错。
22. Kotlin 的 by接口委托
跟基础抽象类类似
Kotlin的扩展函数
就是可以为一个对象,随意添加一个新的函数(静态方法)
fun String.addExclamation(): String {
return "$this!"
}
val message = "Hello"
val modifiedMessage = message.addExclamation() // 扩展函数使用
println(modifiedMessage) // 输出:Hello!
密封类 + 密封接口
其实就是一个“枚举”
sealed interface Shape
class Circle : Shape()
class Rectangle : Shape()
fun draw(shape: Shape) {
when (shape) {
is Circle -> println("Drawing a circle")
is Rectangle -> println("Drawing a rectangle")
}
}
fun main() {
val circle: Shape = Circle()
val rectangle: Shape = Rectangle()
draw(circle) // 输出: Drawing a circle
draw(rectangle) // 输出: Drawing a rectangle
}
对象表达式
就是匿名内部类
interface OnClickListener {
fun onClick()
}
fun setOnClickListener(listener: OnClickListener) {
// Implementation
}
fun main() {
setOnClickListener(object : OnClickListener {
override fun onClick() {
println("Button clicked")
}
})
}
84
3179

被折叠的 条评论
为什么被折叠?



