Andriod开发24000字长文带你入门kotlin

Kotlin 是谷歌大力推广的安卓开发语言,用来取代 java。
因为 java 被甲骨文收购,这个公司肯定会将 java 商业化。这是谷歌推广 kotlin 的初衷。
kotlin 与 java 虚拟机

java 使用 jvm 实现了跨平台,但是 kotlin 的快平台特性比 java 更加强大,它可以直接生成二级制代码运行。

kotlin 的优点
- 缝合各个语言的优点,下一代的安卓开发语言。
- kotlin 可以实现全栈开发

kotlin 的基本语法
变量与打印
只能说和 ts 一模一样。
fun main() {
// 打印语句
println("hello kotlin")
// 变量赋值
var name: String = "lijiajun"
name = "li"
println(name)
}
console
hello kotlin
li
进程已结束,退出代码0
内置的数据类型:
- String -> 字符串
- Char -> 单字符
- Boolean -> 布尔值
- Int -> 整形
- Double -> 小数
- Float -> 小数
- List -> 集合
- Set -> 无重复集合
- Map -> 键值对
在 kotlin 中没有 int ,float 这样的写法,我们的 Int、Float 等等实际上是会被转化成 java 中的 java int 和 java float。
kotlin中的只读变量
val
关键字。
fun main() {
// 这个变量永远不会被修改 -> val
val info: String = "info"
}
kotlin中类型推断
kotlin 会自动推断变量的类型,所以这里的 :String
是多余的。

所以上面的代码可以简化:
fun main() {
// 这个变量永远不会被修改 -> val
val info = "info"
println(info)
}
编译时常量
val 是只读的变量,不是常量。
kotlin 中的常量只能是基本的数据类型。
const val PI = 3.1415
fun main() {
// 这个变量永远不会被修改 -> val
val info = "info"
println(info)
}
编译时常量只能写在函数的外面,在编译时赋值。
查看kotlin编译后的字节码
原来的代码:
const val PI = 3.1415
fun main() {
// 这个变量永远不会被修改 -> val
val info = "info"
println(info)
}
编译后的字节码:
// ================MainKt.class =================
// class version 52.0 (52)
// access flags 0x31
public final class MainKt {
// access flags 0x19
public final static D PI = 3.1415
// access flags 0x19
public final static main()V
L0
LINENUMBER 4 L0
LDC "info"
ASTORE 0
L1
LINENUMBER 5 L1
L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 0
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
L3
L4
LINENUMBER 6 L4
RETURN
L5
LOCALVARIABLE info Ljava/lang/String; L1 L5 0
MAXSTACK = 2
MAXLOCALS = 1
// access flags 0x1009
public static synthetic main([Ljava/lang/String;)V
INVOKESTATIC MainKt.main ()V
RETURN
MAXSTACK = 0
MAXLOCALS = 1
@Lkotlin/Metadata;(mv={1, 8, 0}, k=2, d1={"u0000u000enu0000nu0002u0010u0006nu0000nu0002u0010u0002nu0000u001au0006u0010u0002u001au00020u0003"u000eu0010u0000u001au00020u0001Xu0086Tu00a2u0006u0002nu0000u00a8u0006u0004"}, d2={"PI", "", "main", "", "kotlin_learning"})
// compiled from: Main.kt
}
// ================META-INF/kotlin_learning.kotlin_module =================
• • •
••MainKt" *
因为只有编译之后的字节码才能被 jvm 执行。
kotlin中的引用类型
像这样的写法:
val age:Int = 100
val pi:Float = 3.14.15f
实际上这里面的 Int
、Float
是 kotlin 中定义的类,也就是引用类型。
实际上最后会被转换成 java 的基本类型。
import kotlin.Metadata;
@Metadata(
mv = {1, 8, 0},
k = 2,
d1 = {"u0000u000enu0000nu0002u0010u0006nu0000nu0002u0010u0002nu0000u001au0006u0010u0002u001au00020u0003"u000eu0010u0000u001au00020u0001Xu0086T¢u0006u0002nu0000¨u0006u0004"},
d2 = {"PI", "", "main", "", "kotlin_learning"}
)
public final class MainKt {
public static final double PI = 3.1415;
public static final void main() {
String info = "info";
System.out.println(info);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
所以不用担心代码的性能,但是在转化的时候是肯定的会消耗性能的。
kotlin的range表达式
start..end
包含开头结尾。
import kotlin.random.Random
fun main() {
val randomInt = Random.nextInt(0, 101)
if (randomInt in 10..59) {
println("不及格")
} else if (randomInt in 60..100) {
println("及格啦")
} else {
println("分数太低了")
}
}
kotlin中的when表达式
类似于其他语言中的 switch
表达式。
import kotlin.random.Random
fun main() {
// if (randomInt in 10..59) {
// println("不及格")
// } else if (randomInt in 60..100) {
// println("及格啦")
// } else {
// println("分数太低了")
// }
when (Random.nextInt(0, 101)) {
0 -> println("你考了零蛋")
in 1..59 -> println("你的成绩不合格")
in 60..100 -> println("你的成绩合格")
!in 0..100 -> println("你的成绩有问题")
else -> println("处理其余的情况")
}
}
kotlin中的字符串模板
"${name}"
fun main() {
val age = 123
println("你今年${age}岁了")
}
还可以在其中使用简单的语句:
import kotlin.random.Random
fun main() {
val age = Random.nextInt(0, 200)
println("你今年${age}岁了")
println("啊这,${if (age < 100) "你还年轻" else "你太老了"}")
}
console
你今年90岁了
啊这,你还年轻
进程已结束,退出代码0
如果在 java 中写的话,就只能使用 +
来拼接字符串了。
kotlin的函数
基本的写法
kotlin 的函数也和 ts 十分相似:
fun main() {
method(1, "hello")
}
public fun method(age: Int, name: String): Int {
println("hello func")
return 1
}
注意此处的 public 实际上是冗余的:

默认参数
public fun method(name: String, age: Int = 2): Int {
println("hello func $age")
return 10
}
默认的参数写在最后一个。
具名参数
使用具名参数可以将默认的参数的位置随意放置:
fun main() {
method(name = "hello")
}
public fun method(age: Int = 2, name: String): Int {
println("hello func $age")
return 10
}
Unit函数
就是没有值返回,就像 void 一样。
默认的话不写也是有的。
private fun method1(): Unit {
return 1
}
像上面的这种写法就是有问题的。
Nothing类型
其实就是抛出异常
private fun show(number: Int) {
when (number) {
-1 -> TODO("没有这种分数")
in 0..59 -> println("您的分数不及格")
in 60..70 -> println("您的分数及格")
}
}
TODO:
@kotlin.internal.InlineOnly
public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An operation is not implemented: $reason")
反引号的使用
- 用来测试代码
fun main() {
`这是登录功能 2023年6月1日`("李嘉俊", "ljj")
}private fun `这是登录功能 2023年6月1日`(name: String, pwd: String) {
println("姓名$name,密码$pwd")
} - 解决在使用 java 中的函数,但是像
in
这种在 java 中并不是关键字的单词,是可以作为函数名的,但是在 kotlin 中,in 是关键字,所以需要使用引号
JavaFunc.`in`() - 为了预防反编译

匿名函数Lambda
什么是匿名函数
下面是一个匿名函数的例子:
fun main() {
println("lijiajun".count())
var len = "lijiajun".count() {
it == 'i'
}
println(len)
}
console
8
2
进程已结束,退出代码0
匿名函数隐式返回
最后一行的值默认就是返回值。
fun main() {
// 函数输入输出的声明
val methodAction: () -> String
// 函数的实现
methodAction = {
val temp = 123
// 最后一行的值就是返回值,不需要写 return
"lijiajun$temp"
}
println(methodAction())
}
上面的函数也可以连接起来写:
fun main() {
// 函数的实现
val methodAction: () -> String = {
val temp = 123
// 最后一行的值就是返回值,不需要写 return
"lijiajun$temp"
}
println(methodAction())
}
匿名函数的参数
fun main() {
// 函数的实现
val methodAction: (Int, String, Double) -> String = { p1, p2, p3 ->
val temp = 123
// 最后一行的值就是返回值,不需要写 return
"lijiajun$temp,$p1,$p2,$p3"
}
println(methodAction(1, "whoami", 1.0))
}
it 关键字
我们上面的匿名函数如果只有一个参数的话,我们可以这么写:
fun main() {
// 函数的实现
val methodAction: (Int) -> String = { p1 ->
val temp = 123
// 最后一行的值就是返回值,不需要写 return
"lijiajun$temp,$p1"
}
println(methodAction(1))
}
但是 kotlin 给我们提供了 it
关键字,我们可以这样写:
fun main() {
// 函数的实现
val methodAction: (Int) -> String = {
val temp = 123
// 最后一行的值就是返回值,不需要写 return
"lijiajun$temp,$it"
}
println(methodAction(1))
}
我们可以借助 idea 的提示来看:

匿名函数的类型推断
就是很多时候我们是不需要手动指定返回值的类型的,kotlin 可以帮我们推断:
fun main() {
// 函数的实现
val methodAction = { p1: Int ->
val temp = 123
// 最后一行的值就是返回值,不需要写 return
"lijiajun$temp,$p1"
}
println(methodAction(1))
}
lambda表达式
上述所学的匿名函数就是 lambda 表达式。

kotlin 函数进阶
函数当做参数
其实就是将 lambda 表达式作为函数的参数先进行声明,然后在实际使用的时候写上具体的用法。
import kotlin.math.log
import kotlin.random.Random
fun main() {
loginApi("lijiajun", "123456") { msg: String, code: Int ->
println("$msg,$code")
}
}
// 模拟数据库
const val DATABASE_NAME = "LIJIJUN"
const val DATABASE_PWD = "1223"
// 登录的 api -> 模仿前端的代码
private fun loginApi(username: String, userPwd: String, res: (String, Int) -> Unit) {
if (username == null || userPwd == null) {
TODO("用户名或者密码为空")
}
// 前端校验
if (username.length > 3 && userPwd.length > 3) {
loginApiServer(username, userPwd)
// 登录成功后进行其他的操作...
res("login success", 200)
} else {
res("login fail", 404)
TODO("用户名或者密码不合格")
}
}
private fun loginApiServer(name: String, pwd: String): Boolean {
return name == DATABASE_NAME && pwd == DATABASE_PWD
}
以上的代码仅供学习,实际上逻辑是有问题的。
lambda 表达式作为参数的另外一种实现
实际上也是可以这没写的,虽然好像更合理,但是不建议。
fun main() {
loginApi("lijiajun", "123456", { resMsg: String, msgCode: Int ->
println("$resMsg,$msgCode")
})
}
和上面的
fun main() {
loginApi("lijiajun", "123456") { msg: String, code: Int ->
println("$msg,$code")
}
}
是一样的。
函数内联
如果函数使用了 lambda 参数,就需要声明成内联。
如果不声明内联的话,在调用端会生成多个对象造成性能损耗。
(在 kotlin 代码中并无法看出,但是在编译后的字节码,然后反编译得到的 java 代码中可以看出,性能损耗)
添加内联关键字即可 inline
private inline fun loginApi(username: String, userPwd: String, res: (String, Int) -> Unit) {
if (username == null || userPwd == null) {
TODO("用户名或者密码为空")
}
// 前端校验
if (username.length > 3 && userPwd.length > 3) {
loginApiServer(username, userPwd)
// 登录成功后进行其他的操作...
res("login success", 200)
} else {
res("login fail", 404)
TODO("用户名或者密码不合格")
}
}
其实就相当于 C++ 中的 #define
宏定义,宏替换。
函数引用
可以将 lambda
函数的内容提出来。
形式 ::func
。
可以看出,如果使用了函数引用的话,看起来会更加合理,因为我们根据形式上就可以看出将函数作为第三个参数传入了。
import kotlin.math.log
import kotlin.random.Random
fun main() {
loginApi("lijiajun", "123456", ::func)
}
private fun func(msgString: String, msgCode: Int) {
println("$msgString,$msgCode")
}
// 模拟数据库
const val DATABASE_NAME = "LIJIJUN"
const val DATABASE_PWD = "1223"
// 登录的 api -> 模仿前端的代码
private inline fun loginApi(username: String, userPwd: String, res: (String, Int) -> Unit) {
if (username == null || userPwd == null) {
TODO("用户名或者密码为空")
}
// 前端校验
if (username.length > 3 && userPwd.length > 3) {
loginApiServer(username, userPwd)
// 登录成功后进行其他的操作...
res("login success", 200)
} else {
res("login fail", 404)
TODO("用户名或者密码不合格")
}
}
private fun loginApiServer(name: String, pwd: String): Boolean {
return name == DATABASE_NAME && pwd == DATABASE_PWD
}
将lambda函数作为返回值
fun main() {
val res = show()
println(res(22, 100.0))
}
// 我们返回一个 lambda 函数
private fun show(): (Int, Double) -> String {
val value = "lijiajun"
return { age: Int, grade: Double ->
"$value,年龄是$age,成绩是$grade"
}
}
console
lijiajun,年龄是22,成绩是100.0
进程已结束,退出代码0
匿名函数与具名函数
fun main() {
// 匿名函数
show("lijiajun", 22, '男', "学习kotlin") { res: String ->
println(res)
}
}
private inline fun show(name: String, age: Int, sex: Char, study: String, showRes: (String) -> Unit) {
val str = "$name,$age,$sex,$study"
showRes(str)
}
一下的 {}
中的部分就是 lambda 表达式的实现体,我们也叫作匿名函数。

而以下的方式就是具名函数,就是将 lambda 表达式的实现体提出来单独写一个函数:
fun main() {
// 匿名函数
show("lijiajun", 22, '男', "学习kotlin", ::func1)
}
private fun func1(res: String) {
println(res)
}
private inline fun show(name: String, age: Int, sex: Char, study: String, showRes: (String) -> Unit) {
val str = "$name,$age,$sex,$study"
showRes(str)
}
kotlin 语言的一些特点
kotlin 的可空性
kotlin中的变量除非声明的时候声明了可空,否则不能赋值为null。
写法就是在原来类型后面加上问号即可。
fun main() {
var name: String? = "lijiajun"
name = null
}
kotlin 的安全调用操作符
如果一开始给变量定义了可空,在使用一些 api 的时候就会报错,因为你先要保证变量不为 null 才能使用这个 api,所以需要我们给出补救措施。

补救措施:在变量后面加上?
表示如果是 null 的话,后面就不执行了。
fun main() {
var name: String? = "lijiajun"
println(name?.length)
}
kotlin 使用 let 安全调用
在变量的值确定不是 null 之后,使用 let 执行相关的操作。
fun main() {
var name: String? = "lijiajun"
name = null
var r = name?.let {
// it == name
it
}
println(r)
}
kotlin 非空断言
就是做出补救措施的时候,我们断言它就是不为null,实际上我也不管了,就是必须要执行。
但实际上写的时候 idea 不会让你出错的。:)

if 对空值的补救
其实就是做一个最简单的判断。
fun main() {
var name: String? = "lijiajun"
name = null
if(name !== null) {
}
}
kotlin 空合并操作符
fun main() {
var name: String? = "lijiajun"
name = null
println(name ?: "您的值为null")
}
console
您的值为null
进程已结束,退出代码0
结合let一起使用:
fun main() {
var name: String? = "lijiajun"
name = null
println(name ?: "您的值为null")
println(name?.let { "$it" } ?: "你的值为null")
}
console
您的值为null
你的值为null
进程已结束,退出代码0
kotlin 中的异常处理和自定义异常
使用 try {} catch {} 处理即可。
kotlin 的先决条件函数

kotlin 中常用写法和 api
所有的 api 以下都是简单介绍,具体的使用可以查看官网即可。
substring
裁剪字符串。
str.substring(0,index)
indexOf
str.indexOf('char')
until
直到
str.substring(0,index)
等价于
str.substring(0 until index)
split
// 根据逗号分割字符串
str.split(",")
结构
val (str1,str2) = str.split(",")
replace
val sourcePwd = "hsadksghashdfiahghiwe"
// 对密码进行加密
// 实际上就是对对应的字符进行替换
// 具体请查看官方文档
fun encryptPassword(password: String): String {
return password.replace('a', '1')
.replace('e', '2')
.replace('i', '3')
.replace('o', '4')
.replace('u', '5')
}
fun decryptPassword(encryptedPassword: String): String {
return encryptedPassword.replace('1', 'a')
.replace('2', 'e')
.replace('3', 'i')
.replace('4', 'o')
.replace('5', 'u')
}
== 和 ===
==
: 值的比较
===
:值和类型的比较
注意和 js 中的对比,==
在 js 中是需要先转化再比较的,在 kotlin 中并不会,而是直接比较。
字符串遍历操作
str.forEach{
println("$it")
}
关于 for 循环的操作:
1. for-in循环
for-in循环用于遍历数组、集合或其他可迭代对象。它的语法如下:
```kotlin
for (item in collection) {
// 循环体
}
```
其中,item是集合中的每个元素,collection是要遍历的集合。例如,我们可以使用for-in循环遍历一个整型数组:
```kotlin
val numbers = intArrayOf(1, 2, 3, 4, 5)
for (number in numbers) {
println(number)
}
```
输出:
```
1
2
3
4
5
```
2. for循环
for循环用于执行一定次数的循环。它的语法如下:
```kotlin
for (i in start..end step stepSize) {
// 循环体
}
```
其中,i是循环变量,start是起始值,end是结束值,stepSize是步长。例如,我们可以使用for循环打印出1到10之间的偶数:
```kotlin
for (i in 1..10 step 2) {
println(i)
}
```
输出:
```
1
3
5
7
9
```
在for循环中,我们还可以使用downTo和until关键字来控制循环的方向和结束条件。例如,我们可以使用downTo关键字打印出10到1之间的数:
```kotlin
for (i in 10 downTo 1) {
println(i)
}
```
输出:
```
10
9
8
7
6
5
4
3
2
1
```
还可以使用until关键字指定结束值不包含在循环中:
```kotlin
for (i in 0 until 10) {
println(i)
}
```
输出:
```
0
1
2
3
4
5
6
7
8
9
```
toInt
将其他类型转为 Int,小数去掉
"666".toInt()
// 严谨的写法需要判断是否为空,并且使用 toIntOrNull
val num:Int? = "666.6".toIntOrNull()
roundToInt
四舍五入
apply
- 没有 it
- 有一个 this,就是字符串本身
- 函数的返回值就是字符串本身
run
- 函数的返回值的类型就最后一行的值的类型
- 有一个 this,就是字符串本身
with
also
takeIf
takeUnless
kotlin 数据结构之集合
集合 List
创建集合:
fun main() {
val list: List<String> = listOf("lijiajun", "zhangsan", "lisi")
println(list)
}
根据我们的类型推断,可以简写:
fun main() {
val list = listOf("lijiajun", "zhangsan", "lisi")
println(list)
}
为了防止访问越界:
使用 getOrElse
解决越界的问题
fun main() {
val list = listOf("lijiajun", "zhangsan", "lisi")
println(list.getOrElse(4) { "越界" })
}
console
越界
进程已结束,退出代码0
使用getOrNull
解决越界问题
fun main() {
val list = listOf("lijiajun", "zhangsan", "lisi")
println(list.getOrNull(4))
}
console
null
进程已结束,退出代码0
结合空合并运算符:
fun main() {
val list = listOf("lijiajun", "zhangsan", "lisi")
println(list.getOrNull(4) ?: "你越界了")
}
我们尽量选择使用 getOrElse
可变 List 集合
普通的 List
集合是无法完成有关改变的操作的。
mutableListOf
fun main() {
val list: MutableList<String> = mutableListOf("lijiajun", "zhangsan")
println(list)
list.add("lisi")
println(list)
list.remove("lijiajun")
println(list)
}
console
[lijiajun, zhangsan]
[lijiajun, zhangsan, lisi]
[zhangsan, lisi]
进程已结束,退出代码0
可以利用 toMutableList
将不可变集合变为可变的集合:
fun main() {
val list: List<String> = listOf("zhangsan")
val list2: MutableList<String> = list.toMutableList()
list2.remove("zhangsan")
println(list2)
}
mutator 函数
就是一些关于增加和减少元素的简便操作:
fun main() {
val list = mutableListOf<String>("zhangsan")
list += "lisi"
list += "wangwu"
println(list)
list -= "lisi"
println(list)
}
console
[zhangsan, lisi, wangwu]
[zhangsan, wangwu]
进程已结束,退出代码0
removeIf 函数
条件移出:
1.删除所有元素
fun main() {
val list = mutableListOf<String>("zhangsan")
list.removeIf { true }
println(list)
}
2.删除某一个指定的元素
fun main() {
val list = mutableListOf<String>("zhangsan")
list.removeIf { item -> item.contains("zhangsan") }
println(list)
}
遍历集合
使用 for 循环
fun main() {
val list = mutableListOf<String>("zhangsan", "lisi", "wangwu")
for (i in list) {
println(i)
}
}
使用 forEach
fun main() {
val list = mutableListOf<String>("zhangsan", "lisi", "wangwu")
list.forEach { item ->
println(item)
}
}
使用 forEachIndexed(有下标)
fun main() {
val list = mutableListOf<String>("zhangsan", "lisi", "wangwu")
list.forEachIndexed { index, item ->
println("$index,$item")
}
}
Set 集合
会自动帮我们去重
fun main() {
val set: Set<String> = setOf("zhangsan", "lisi", "wangwu", "lisi")
println(set)
}
set取值的话是不能够通过单纯的下标来取,我们通过 elementAt
来取:
fun main() {
val set: Set<String> = setOf("zhangsan", "lisi", "wangwu", "lisi")
println(set.elementAt(0))
}
处理越界的方法和 List 都是类似的:
println(set.elementAtOrElse(100) { "越界了" })
kotlin 数据结构之数组
数组的创建和越界访问
所有的数组类型:

fun main() {
val arr: IntArray = intArrayOf(1, 2)
println(arr[0])
println(arr.elementAtOrElse(100) { -1 })
}
集合转数组
to...Array()
fun main() {
val list: List<String> = listOf("lijiajun", "zhangsan")
val arr = list.toTypedArray()
println(arr[0])
}
对象类型数组
val arr: Array<File> = arrayOf(File("123"))
kotlin 数据结构之 Map
创建map
1.直接创建
fun main() {
val map: Map<String, Int> = mapOf("lijiajun" to 123, "lisi" to 123)
}
2.使用Pair
fun main() {
val map = mapOf(Pair("lijiajun", 123), Pair("lisi", 123))
}
kotlin 面向对象
面向对象案例
以洗衣服为例子:
fun main() {
var wash = WashMachine("小天鹅", 12)
wash.open()
println("将需要轻柔洗的衣服放进去")
wash.close()
wash.currentMode = 1
wash.start()
}
class WashMachine(var modele: String, var size: Int) {
private var isOpen: Boolean = false
var currentMode: Int = 0
fun open() {
println("打开洗衣机")
isOpen = true
}
fun close() {
println("洗衣机的关门")
isOpen = false
}
fun start() {
if (!isOpen) {
when (currentMode) {
0 -> println("现在是初始模式,请选择一个模式")
1 -> {
println("放水")
println("轻柔模式")
}
2 -> {
println("放水")
println("狂揉模式")
}
else -> println("模式无法识别")
}
} else {
println("请关门")
}
}
fun selectMode(mode: Int) {
when (mode) {
0 -> println("初识模式,请您选择模式")
1 -> println("轻柔")
2 -> println("狂揉")
else -> println("不要乱动")
}
}
}
封装
像下面这种不需要在对象外面访问的函数或者变量,可以加上 private
关键字。
private fun setMotorSpeed(speed: Int) {
println("当前发动机的转速${speed}")
}
继承
将父类前面加上 open
关键字,然后子类才可以继承:
fun main() {
var son: Son = Son()
son.action()
}
open class Father {
var charactor: String = "性格内向"
fun action() {
println("在公共场合大声说话")
}
}
class Son : Father() {
}
虽然子类中什么都没有,但是依然可以直接使用父类的方法
console
在公共场合大声说话
进程已结束,退出代码0
重写方法
需要父类将方法前面加上: open
fun main() {
var son: Son = Son()
son.action()
}
open class Father {
var charactor: String = "性格内向"
open fun action() {
println("在公共场合大声说话")
}
}
class Son : Father() {
override fun action() {
println("儿子不在公众场合说话")
}
}
抽象类和继承
就是有的类是有相同的部分的:
- 人类(相同:吃饭,睡觉... 不同:上厕所...)
- 男人
- 女人
fun main() {
var huaxingyu: Man = Man("huaxingyu")
var person: Woman = Woman("awoman")
huaxingyu.pee()
person.pee()
}
// 抽象类
abstract class Human(var name: String) {
abstract fun eat()
abstract fun pee()
}
// 男人
class Man(name: String) : Human(name) {
override fun eat() {
println("$name,吃饭")
}
override fun pee() {
println("$name,站着")
}
}
// 女人
class Woman(name: String) : Human(name) {
override fun eat() {
println("$name,吃饭")
}
override fun pee() {
println("$name,蹲着")
}
}
console
huaxingyu,站着
awoman,蹲着
进程已结束,退出代码0
抽象类与接口
接口:接口泛指实体把自己提供给外界的一种抽象化物(可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被内部修改而不影响外界其他实体与其交互的方式。
interface
fun main() {
var man: Man = Man()
man.eat()
}
interface IMan {
fun eat()
}
class Man : IMan {
override fun eat() {
println("吃饭")
}
}
接口与抽象类的区别:
- 接口是事物的能力
- 抽象类反应的是事物的本质
class Man : IMan, Human() {
override fun eat() {
println("吃饭")
}
override fun fertility() {
println("生育")
}
}
class Taijian : Human() {
override fun eat() {
println("吃饭")
}
}
太监和男人都是人,都可以吃饭(抽象类),但是太监不能够生育(接口),也就是太监没有继承生育的这个接口,虽然我们可以给他加,但是很明显这条个是不合理的。
委托和代理
by
-> 代理
原来的代码,父亲和儿子都可以洗碗
fun main() {
val son: BigHeadSon = BigHeadSon()
val father: SmallHeadFather = SmallHeadFather()
son.washing()
father.washing()
}
// 洗完的能力
interface IWashBow {
fun washing()
}
class BigHeadSon : IWashBow {
override fun washing() {
println("我是大头儿子,我在洗碗")
}
}
class SmallHeadFather : IWashBow {
override fun washing() {
println("我是小头爸爸,我在洗碗")
}
}
使用代理,父亲让儿子帮自己洗碗
使用这样的写法:
class SmallHeadFather : IWashBow by BigHeadSon() {}
fun main() {
val son: BigHeadSon = BigHeadSon()
val father: SmallHeadFather = SmallHeadFather()
son.washing()
father.washing()
}
// 洗完的能力
interface IWashBow {
fun washing()
}
class BigHeadSon : IWashBow {
override fun washing() {
println("我是大头儿子,我在洗碗")
}
}
// class SmallHeadFather : IWashBow {
// override fun washing() {
// println("我是小头爸爸,我在洗碗")
// }
// }
class SmallHeadFather : IWashBow by BigHeadSon() {}
console
我是大头儿子,我在洗碗
我是大头儿子,我在洗碗
进程已结束,退出代码0
单例模式
关于单例模式:
单例类是一种特殊的类,它只能创建一个实例。在 Kotlin 中,您可以使用“object”关键字来定义单例类。这个实例是在第一次访问单例类的时候创建的,并且在整个应用程序中只有一个实例。
因为单例类只能创建一个实例,所以您不能使用构造函数来创建新的实例。相反,您可以使用类名来访问单例类的唯一实例。例如:
```
object MySingleton {
fun doSomething() {
// ...
}
}
// 使用
MySingleton.doSomething()
```
在这个例子中,MySingleton 是一个单例类,它只有一个实例。您可以使用 MySingleton.doSomething() 来访问这个唯一的实例并调用其方法。
代码案例:
fun main() {
val father: SmallHeadFather = SmallHeadFather()
father.washing()
}
// 洗完的能力
interface IWashBow {
fun washing()
}
object BigHeadSon : IWashBow {
override fun washing() {
println("我是大头儿子,我在洗碗")
}
}
// class SmallHeadFather : IWashBow {
// override fun washing() {
// println("我是小头爸爸,我在洗碗")
// }
// }
class SmallHeadFather : IWashBow by BigHeadSon {}
枚举
就是做一个映射
fun main() {
println(Work.周一)
println(Work.周一.ordinal)
}
enum class Work {
周一, 周二, 周三, 周四, 周五, 周六, 周日
}
周一
0
进程已结束,退出代码0
密封类 Sealed class

在子类类型有限的 class ,我们可以使用密封类,或者叫做印章类。
fun main() {
var son: Son.小驴 = Son.小驴()
son.sayHello()
}
sealed class Son {
fun sayHello() {
println("大家好")
}
class 小驴() : Son()
class 骡子() : Son()
}