Attention:
- 本文章适用于有编程基础的人,如果有多种编程语言基础,例如Swift,会发现很多相似之处,学习起来也会比较容易
- 本文章属于笔记,没有知识讲解条理,不太适合透彻学习Kotlin,纯属写代码时的参考
一、基础
基本内置数据类型
- String
- Char
- Boolean --> Java boolean
- Int --> Java int
- Double --> Java double
- Float --> Java float
- List
- Set
- Map
Kotlin中一些引用类型编译器会转化为Java的基本类型,不会有额外的性能消耗
编译时常量
const val PI = 3.1415
- 编译时常量只能是常用的基本数据类型:(String, Double, Int, Float, Long, Short, Byte, Char, Boolean)
- 编译时常量只能定义在函数外(如果在函数内定义的话就必须在运行时才能调用函数赋值,就不能称是编译时常量了,所以编译时常量只能定义在函数之外,这样就能在编译期间就初始化了)
range表达式
when表达式
表达式有返回值,语句没有返回值
val week = 5
val info = when(week) {
1 -> "今天是星期1"
2 -> "今天是星期2"
3 -> "今天是星期3"
4 -> "今天是星期4"
5 -> "今天是星期5"
// 6 -> true // when返回Any类型
else -> "今天是周末" // when返回String类型
// else -> { // when返回Any类型
// println("Any")
// }
}
println(info)
String模版
val park = "黄石公园"
val time = 6
println("今天去${park}玩了$time 个小时") // $变量名 后面没有字符串(标点符号、空格可以)的可以不加{}
val isLogin = true
println("${if (isLogin) "登录成功" else "登录失败,请重试"}")
函数
函数参数的默认值
fun action1(name: String, age: Int) {
println("姓名;$name, 年龄:$age")
}
fun action2(name: String, age: Int = 88) {
println("姓名;$name, 年龄:$age")
}
具名函数参数
fun main() {
loginAction(age = 30, name = "test", phone = "12345", pwd = "123")
}
fun loginAction(name: String, pwd: String, age: Int, phone: String) {
println("name: $name, pwd: $pwd, age: $age, phone: $phone")
}
Unit函数特点
Nothing类型特点
TODO() // 终止程序继续执行,抛出一个异常
fun main() {
show(-1)
}
private fun show(number: Int) {
when(number) {
-1 -> TODO("分数不合法")
in 0..59 -> println("不及格")
in 60..70 -> println("及格")
in 71..100 -> println("优秀")
}
}
带反引号函数名特点
fun main() {
// 第一种情况:函数特殊命名
`登录功能测试 需求实现人张三`("test", "pwd")
// 第二种情况:函数命名使用了Kotlin中的关键词
JavaTest.`is`()
JavaTest.`in`()
// 第三种情况:函数名违反命名规则,反编译时导致崩溃防止反编译
`655435383`()
}
private fun `655435383`() {
}
// 文档中注释
// 防止反编译的函数`655435383`,作用是xxx
二、语法糖
匿名函数
fun main() {
val len = "test".count()
println(len) // 4
val len2 = "test".count {
it == 't'
}
println(len2) // 2
}
匿名函数-函数类型&隐式返回
fun main() {
// 1.函数输入输出的声明
val methodAction: () -> String
// 2.对上面声明函数的实现
methodAction = {
val name = "name"
"返回值:$name" //最后一行就是函数的返回值
}
// 3.调用
println(methodAction())
}
匿名函数-函数参数
fun main() {
val methodAction: (Int, Int, Int) -> String = { number1, number2, number3 ->
val name = "name"
"$name: $number1, $number2, $number3"
}
println(methodAction(10, 20, 30))
}
匿名函数-it关键字
匿名函数的参数只有一个的话,会自动生成参数名it,代表被传入的参数对象
fun main() {
val methodAction: (String) -> String = {
"$it"
}
println(methodAction("name"))
val methodAction2: (Double) -> String = {
"$it"
}
println(methodAction2(123.4))
}
匿名函数-类型推断
fun main() {
val method1 = { v1: Double, v2: Float, v3: Int ->
"v1:$v1, v2:$v2, v3:$v3"
} // (Double, Float, Int) -> String
println(method1(12.3, 45.6f, 10))
val method2 = {
123.0f
} // () -> Unit
println(method2())
val method3 = { number: Int ->
number
} // (Int) -> Int
println(method3(1))
}
在函数中定义参数是函数的参数
fun main() {
loginApi("name", "pwdd") { msg: String, code: Int ->
println("msg: $msg, code: $code")
}
}
const val USER_NAME = "name"
const val USER_PWD = "pwdd"
fun loginApi(username: String?, userpwd: String?, responseResult: (String, Int) -> Unit) {
if (username == null || userpwd == null) {
TODO("用户名或密码为null")
}
if (username.length > 3 && userpwd.length > 3) {
if (webLoginApi(username, userpwd)) {
responseResult("成功", 200)
} else {
responseResult("失败", 400)
}
} else {
TODO("用户名或密码不合格")
}
}
private fun webLoginApi(name: String?, pwd: String?): Boolean {
return name == USER_NAME && pwd == USER_PWD
}
简略写法
fun main() {
// 第一种方式
loginApi("name", "pwdd", { msg: String, code: Int ->
println("msg: $msg, code: $code")
})
// 第二种方式
loginApi("name", "pwdd", responseResult = { msg: String, code: Int ->
println("msg: $msg, code: $code")
})
// 第三种方式,简略写法
loginApi("name", "pwdd") { msg: String, code: Int ->
println("msg: $msg, code: $code")
}
}
内联函数
inline
如果函数使用lambda做为参数,需要声明成内联,否则在调用时会生成多个对象来完成lambda的调用,引起性能损耗。
如果函数使用内联,相当于C++的#define 宏定义 宏替换,会把代码替换到调用处,没有任何新建函数、新建对象的损耗
fun main() {
loginApi("name", "pwdd") { msg: String, code: Int ->
println("msg: $msg, code: $code")
}
}
const val USER_NAME = "name"
const val USER_PWD = "pwdd"
inline fun loginApi(username: String?, userpwd: String?, responseResult: (String, Int) -> Unit) {
if (username == null || userpwd == null) {
TODO("用户名或密码为null")
}
if (username.length > 3 && userpwd.length > 3) {
if (webLoginApi(username, userpwd)) {
responseResult("成功", 200)
} else {
responseResult("失败", 400)
}
} else {
TODO("用户名或密码不合格")
}
}
fun webLoginApi(name: String?, pwd: String?): Boolean {
return name == USER_NAME && pwd == USER_PWD
}
函数引用
lambda属于函数类型的 对象,在使用函数作为参数时,需要把普通函数通过 :: 转为函数类型的对象
fun main() {
login("name", "pwdd", ::printResult)
// 或
val obj = ::printResult
login("name", "pwdd", obj)
}
const val USER_NAME = "name"
const val USER_PWD = "pwdd"
fun printResult(msg: String, code: Int) {
println("结果:msg: $msg, code: $code")
}
inline fun login(username: String, pwd: String, responseResult: (String, Int) -> Unit) {
if (username == USER_NAME && pwd == USER_PWD) {
responseResult("成功", 200)
} else {
responseResult("失败", 400)
}
}
使用函数做为返回类型
fun main() {
val result = showMethod("info")
println(result("张三", 30))
}
fun showMethod(info: String): (String, Int) -> String {
println("show info: $info")
return { name: String, age: Int ->
"我是匿名函数:name: $name, age: $age"
}
}
匿名函数与具名函数
fun main() {
// 匿名函数
showPersonInfo("张三", 30) {
println("$it")
}
// 具名函数
showPersonInfo("张三", 30, ::showResultImpl)
}
fun showResultImpl(info: String) {
println(info)
}
fun showPersonInfo(name: String, age: Int, showResult: (String) -> Unit) {
val str = "name: $name, age: $age"
showResult(str)
}
三、Kotlin内置函数
Kotlin可空性特点
?
安全调用操作符
?
带let的安全调用
fun main() {
var name: String? = null
// name = "张三"
val result = name?.let {
// it == name 本身
// 如果能执行到这里,it 一定不为 null
if (it.isBlank()) {
"default"
} else {
"[$it]"
}
}
println(result) // null
}
非空断言操作符
!!
空合并操作符
?:
异常处理与自定义异常
fun main() {
try {
var name: String? = null
checkException(name)
println(name!!.length)
} catch (e: Exception) {
println("$e")
}
}
fun checkException(name: String?) {
name ?: throw CustomException()
}
class CustomException: IllegalArgumentException("代码不严谨")
先决条件函数
checkNotNull()
requireNotNull()
require()
substring
split
fun main() {
val text = "A,B,C,D"
val list = text.split(",")
// 直接输出
println(list)
// 解构,C++中也有解构
val(v1, v2, v3, v4) = list
println("v1:$v1, v2:$v2, v3:$v3, v4:$v4")
}
replace
fun main() {
val sourcePwd = "ABCDEFGHI"
val newPwd = sourcePwd.replace(Regex("[ACF]")) {
when (it.value) {
"A" -> "1"
"C" -> "2"
"F" -> "3"
else -> it.value
}
}
println(newPwd)
}
== 与 === 比较操作
== 值比较
=== 引用比较
数字类型的安全转换函数
toXxxOrNull()
fun main() {
val number = "888.8".toIntOrNull()
println(number ?: "转换错误")
}
Double 转 Int
fun main() {
println(123.4567.toInt()) // 123, 只保留整数
println(123.4567.roundToInt()) // 123, 四舍五入
println(123.5567.toInt()) // 123
println(123.5567.roundToInt()) // 124
val r = "%.2f".format(123.4567)
println(r) // 123.46, 四舍五入
}
apply 内置函数
1.匿名函数中自动生成this参数,代表对象本身
2.apply函数始终返回的是对象本身,一般用于链式调用
val file = File("文件路径")
file.setExecutable(true)
file.setReadable(true)
println(file.readLine())
// 等价于
val file = File("文件路径")
file.apply {
// apply函数默认生成一个代表当前对象的this
setExecutable(true)
}.apply {
setReadable(true)
}.apply {
println(readlnOrNull())
}
let 内置函数
1.匿名函数中自动生成it参数,代表对象本身
2.最后一行做为返回值
fun main() {
val result = listOf(6, 4, 7, 9).let {
// it == 对象本身
// 最后一行做为返回值
it.first() + it.first()
}
println(result)
val result2 = getMethod(null)
println(result2)
}
fun getMethod(value: String?): String {
return value?.let {
it
} ?: "传值为null"
}
run 内置函数
1.匿名函数中自动生成this参数,代表对象本身,一般用于值类型发生改变的链式调用
2.最后一行做为返回值
fun main() {
val str = "12345"
// run 与 具名函数
str
.run(::isLengthOk)
.run(::showText)
.run(::mapText)
.run(::println)
// run 与 匿名函数
str
.run {
length > 3
}
.run {
if (this) "长度ok" else "长度不够"
}
.run {
"[$this]"
}
.run {
println(this)
}
}
fun isLengthOk(str: String) = str.length > 3
fun showText(isLengthOK: Boolean) = if (isLengthOK) "长度ok" else "长度不够"
fun mapText(showText: String) = "[$showText]"
with 内置函数
1.匿名函数中自动生成this参数,代表对象本身,一般用于值类型发生改变的链式调用
2.最后一行做为返回值
与 run 函数基本相同,只是调用方式不同
fun main() {
val str = "12345"
// with 与 具名函数
val r1 = with(str, ::isLengthOk)
val r2 = with(r1, :: showText)
val r3 = with(r2, ::mapText)
with(r3, ::println)
// with 与 匿名函数
val r11 = with(str) {
length > 3
}
val r22 = with(r11) {
if (this) "长度ok" else "长度不够"
}
val r33 = with(r22) {
"[$this]"
}
with(r33) {
println(this)
}
}
fun isLengthOk(str: String) = str.length > 3
fun showText(isLengthOK: Boolean) = if (isLengthOK) "长度ok" else "长度不够"
fun mapText(showText: String) = "[$showText]"
also 内置函数
1.匿名函数中自动生成it参数,代表对象本身
2.also函数始终返回的是对象本身,一般用于链式调用
fun main() {
val file = File("文件路径")
file
.also {
it.setExecutable(true)
it.setReadable(true)
}
.also {
println(it.readLines())
}
}
takeIf 内置函数
如果所传递函数对象中的条件满足,就返回自身
一般情况下,takeIf + 空合并操作符 一起使用
fun main() {
val result = checkPermission("root", "12345")
println(result)
}
fun checkPermission(username: String, userpwd: String): String {
// 一般情况下,takeIf + 空合并操作符 一起使用
return username.takeIf { checkInternal(username, userpwd) } ?: "普通用户"
}
private fun checkInternal(username: String, userpwd: String): Boolean {
return username == "root" && userpwd == "12345"
}
takeUnless 内置函数
如果所传递函数对象中的条件不满足,就返回自身
与 takeIf 功能相反
takeUnless+对象中的属性判空一般一起使用,可以验证对象的属性有没有设置值
class Manager {
private var info: String? = null
fun getInfo() = info
fun setInfo(info: String) {
this.info = info
}
}
fun main() {
val manager = Manager()
// takeUnless+对象中的属性判空一般一起使用,可以验证对象的属性有没有设置值
val r = manager.getInfo().takeUnless { it.isNullOrBlank() } ?: "未初始化"
println(r)
}
四、集合与对象
List
尽量使用 getOrElse 或 getOrNull 获取数组中元素
fun main() {
val list = listOf("A", "B", "C")
println(list[0])
println(list.getOrElse(4) { "越界" })
println(list.getOrNull(4) ?: "越界")
}
可变 List
fun main() {
val list = mutableListOf("A", "B", "C")
list.add("D")
list.remove("D")
val list2 = listOf(1, 2, 3)
// list2.add // 没有这个函数
// 不可变 List 转 可变 List
val list3 = list2.toMutableList()
// 可变 List 转 不可变 List
val list4 = list.toList()
}
mutator函数、removeIf
+= -=,靠运算符重载实现
removeIf 有条件的移除List中元素,会自动遍历元素
fun main() {
val list = mutableListOf("Zhangsan", "Lisi", "Zhangsi", "Wangwu")
// mutator: += -=
list += "赵六"
list -= "Zhangsi"
println(list)
// removeIf
list.removeIf { it.contains("Zhang") }
println(list)
}
List 遍历
fun main() {
val list = listOf(1, 2, 3, 4, 5)
// 第一种
for (item in list) {
println(item)
}
// 第二种
list.forEach {
println(it)
}
// 第三种,带位置索引
list.forEachIndexed { index, item ->
println("index: $index, item: $item")
}
}
解构语法过滤元素
fun main() {
val list = listOf("Zhangsan", "Lisi", "Wangwu")
// 解构,元素值不可变
val(value1, value2, value3) = list
println("value1: $value1, value2: $value2, value3: $value3")
// 解构,元素值可变
var(v1, v2, v3) = list
v1 = "ok"
println("v1: $v1, v2: $v2, v1: $v3")
// 解构时过滤元素,可以节省一点性能
val(_, n2, n3) = list
println("n2: $n2, n3: $n3") // 没有第一个
}
Set
不会出现重复元素
尽量使用 elementAtOrElse 或 elementAtOrNull 获取元素
fun main() {
val set = setOf("A", "B", "C", "A")
println(set.elementAt(0))
// println(set.elementAt(3)) // 崩溃
println(set.elementAtOrElse(0) { "越界" })
println(set.elementAtOrElse(3) { "越界" })
println(set.elementAtOrNull(1) ?: "越界")
println(set.elementAtOrNull(3) ?: "越界")
}
可变 Set
fun main() {
val set = mutableSetOf("A", "B", "C", "A")
set += "D"
set -= "D"
set.add("D")
set.remove("D")
set.removeIf {
it == "D"
}
}
集合转换与快捷函数
fun main() {
val list = listOf("A", "A", "B", "C")
// List 转 Set 去重
val set = list.toSet()
println(set)
// List 转 Set 转 List 去重
val list2 = list.toSet().toList()
println(list2)
// 快捷函数 distinct 去重
val list3 = list.distinct() // 内部:转 可变Set 转 List
println(list3)
}
数组
尽量使用elementAtOrElse 或 elementAtOrNull 获取元素
Kotlin语言中的各种数组类型,虽然是引用类型,背后可以编译成Java基本数据类型
IntArray intArrayOf
DoubleArray doubleArrayOf
LongArray longArrayOf
ShortArray shortArrayOf
ByteArray byteArrayOf
FloatArray floatArrayOf
BooleanArray booleanArrayOf
Array arrayOf 对象数组
fun main() {
val intArray = intArrayOf(1, 2, 3)
// 取元素
intArray.elementAt(0)
// intArray.elementAt(3) // 崩溃
intArray.elementAtOrElse(3) { -1 }
intArray.elementAtOrNull(3) ?: "越界"
// List 转 数组
val charArray = listOf('A', 'B').toCharArray()
// 对象数组
val objArray = arrayOf(File("1"), File("2"))
}
Map
fun main() {
val map1 = mapOf("A" to(1.0), "B" to 2.0)
val map2 = mapOf(Pair("A", 1.0), Pair("B", 2.0))
}
Map 中值的获取
尽量使用 getOrDefault 或 getOrElse
fun main() {
val map = mapOf("A" to (1.0), "B" to 2.0)
// 方式一 [],等价于 get,获取不到返回 null
println(map["A"])
println(map.get("A"))
println(map["C"])
// 方式二 getOrDefault
println(map.getOrDefault("A", -1.0))
println(map.getOrDefault("C", -1.0))
// 方式二 getOrDefault
println(map.getOrElse("A") { "找不到" })
println(map.getOrElse("C") { "找不到" })
// 方式三 getValue,获取不到会崩溃
println(map.getValue("A"))
println(map.getValue("C"))
}
Map 的遍历
fun main() {
val map = mapOf("A" to (1.0), "B" to 2.0)
// 方式一
map.forEach {
println("key: ${it.key}, value: ${it.value}")
}
// 第二种
map.forEach { key, value ->
println("key: $key, value: $value")
}
// 第三种
map.forEach { (key, value) ->
println("key: $key, value: $value")
}
// 第四种
for (item in map) {
println("key: ${item.key}, value: ${item.value}")
}
}
可变 Map
fun main() {
val map = mutableMapOf("A" to (1.0), "B" to 2.0)
// 操作:+= -= [] put
map += "C" to 3.0
map -= "C"
map["C"] = 3.0
map.put("C", 3.0) // 等价于 []
// getOrPut 没有的话就添加进去
map.getOrPut("D") { 4.0 }
println(map["D"])
// getOrPut 有的话就取出来
map.getOrPut("A") { 4.0 }
// getOrDefault 没有就取传递的默认值
val r = map.getOrDefault("F", 6.0)
println(r)
}
定义类、field关键词
对于已经赋初始值的属性,field 代表当前属性的值
class KtBaseClass {
var name = "zhangsan"
// 自动生成隐式代码,写不写都有
get() = field
set(value) {
field = value
}
var info = "is ok"
// 重写隐式代码,扩展功能
get() = field.capitalize()
set(value) {
field = "**${value}**"
}
}
类的计算属性 与 防范竞态条件
计算属性:使用get函数覆盖field的写法
防范竞态条件:当被调用的属性可能为null时,需要使用防空指针的写法编写代码,这种写法称为防范竞态条件
class KtBaseClass {
val number: Int
// 计算属性,get函数覆盖了field,在对应的java代码中,也不会生成对应名称的属性
get() = (1..1000).shuffled().first()
val info: String? = null
// 防范竞态条件,当被调用的成员可能为null时,就必须使用防范竞态条件
fun getShowInfo(): String {
// 这种写法就是防范竞态条件,这种写法会大量使用
return info?.let {
if (it.isBlank()) {
"info 是 空值"
} else {
"info 结果:$it"
}
} ?: "info 是 null"
}
}
类的主构造函数
// 主构造函数:隐式自动生成,不需要写
class KtBaseClass() {
}
// 主构造函数:传入的值命名要按照 _xxx 的方式,传入的值不能直接使用,需要接收后才能使用
class KtBaseClass(_name: String, _sex: Char, _age: Int) {
var name = _name
get() = field // get不允许private
private set(value) {
field = value
}
val sex = _sex
get() = field
// set(value) {} // 声明为val不可修改,没有set
var age = _age
get() = if (field < 0) 0 else field
// 或
// get() {
// return if (field < 0) {
// 0
// } else {
// field
// }
// }
fun show() {
// println(_name) // 不允许直接使用
}
}
fun main() {
val p = KtBaseClass("zhangsan", 'm', -1)
println(p.age)
}
在类的主构造函数中定义属性
// 在主构造函数中直接定义属性
class KtBaseClass(var name: String, val sex: Char, var age: Int) {
fun show() {
println(name)
}
}
类的次构造函数
class KtBaseClass(var name: String) { // 主构造
constructor(name: String, sex: Char) : this(name) { // 次构造函数必须调用主构造函数
println("次构造函数:name: $name, sex: $sex")
}
}
fun main() {
val p = KtBaseClass("zhangsan", 'm')
println(p.name)
}
构造函数中参数的默认值
class KtBaseClass(var name: String = "zhangsan") { // 主构造
constructor(name: String = "zhangsan", sex: Char = 'm') : this(name) { // 次构造函数必须调用主构造函数
println("次构造函数:name: $name, sex: $sex")
}
}
fun main() {
val p = KtBaseClass("zhangsan", 'm') // 调用次构造函数
println(p.name)
val p2 = KtBaseClass() // 优先调用主构造函数
}
类的初始化块
class KtBaseClass(_name: String) { // 主构造
// 相当于Java的 {} 构造代码块,在主构造函数执行时执行
// 可以使用构造函数传入的变量
init {
println("主构造函数调用:name: $_name")
// 可以在这里判断传入值的合法性,验证不通过会抛出异常
require(_name.isNotBlank()) {
"name不能是空值"
}
}
constructor(name: String, sex: Char) : this(name) { // 次构造函数必须调用主构造函数
println("次构造函数:name: $name, sex: $sex")
}
}
fun main() {
KtBaseClass("zhangsan", 'm') // 调用次构造函数
}
构造初始化顺序
// 第一步:生成 val sex
class KtBaseClass(_name: String, val sex: Char) { // 主构造
// 第二步:生成 val name。与init代码块平级,写在init代码块前所以先执行
val name = _name
init {
val nameValue = _name // 第三步:生成 val nameValue
println("init代码块调用:name: $_name")
}
constructor(name: String, sex: Char, age: Int) : this(name, sex) { // 次构造函数必须调用主构造函数
// 第五步:执行次构造函数
println("次构造函数:name: $name, sex: $sex, age: $age")
}
// 第四步:生成 val info
val info = "test info"
}
fun main() {
KtBaseClass("zhangsan", 'm') // 调用次构造函数
}
延迟初始化 lazyinit
使用 lazyinit 的属性前需要手动初始化
class KtClass {
lateinit var info: String
fun loadInfo() {
info = "初始化成功"
}
fun showInfo() {
// if (info == null) {} // 不能这样用,在info初始化前任何使用都会崩溃
if (::info.isInitialized) {
println("info: $info")
} else {
println("info还没初始化")
}
}
}
fun main() {
val p = KtClass()
// println(p.info) // lazyinit的属性在初始化前使用会导致崩溃
// p.loadInfo()
// println(p.info)
p.showInfo()
}
惰性初始化 by lazy
属性在使用时自动初始化
class KtClass {
// 普通方式(饿汉式)
val info1 = readFromDB1()
val info2 by lazy { readFromDB2() }
fun readFromDB1(): String {
println("1开始读取数据")
println("1读取数据中...")
println("1读取数据中...")
println("1读取数据中...")
println("1读取数据中...")
println("1结束读取数据")
return "info1 load success"
}
fun readFromDB2(): String {
println("2开始读取数据")
println("2读取数据中...")
println("2读取数据中...")
println("2读取数据中...")
println("2读取数据中...")
println("2结束读取数据")
return "info2 load success"
}
}
fun main() {
val p = KtClass()
Thread.sleep(5000L)
println("即将开始使用info1")
println("读取到的数据:info: ${p.info1}")
println()
println("即将开始使用info2")
println("读取到的数据:info: ${p.info2}")
}
// 输出
1开始读取数据
1读取数据中...
1读取数据中...
1读取数据中...
1读取数据中...
1结束读取数据
即将开始使用info1
读取到的数据:info: info1 load success
即将开始使用info2
2开始读取数据
2读取数据中...
2读取数据中...
2读取数据中...
2读取数据中...
2结束读取数据
读取到的数据:info: info2 load success
初始化陷阱
注意属性与init代码块定义代码顺序
class KtClass2 {
init {
// 与 var name 生成处于同一优先级,执行顺序与定义顺序有关,所以不能这样用,必须先定义 val number
number = number.times(9)
}
var number = 9
}
初始化陷阱2
class KtClass3 {
var info: String
init {
getInfoMethod() // info 还没初始化就调用了
info = "test info" // 需要把这行放在上一行上面
}
fun getInfoMethod() {
println("info: ${info[0]}")
}
}
fun main() {
KtClass3() // 会崩溃
}
初始化陷阱3
class KtClass5(_info: String) {
var content: String = getInfoContent()
var info = _info // 需要把这行放到最前面
fun getInfoContent() = info
}
fun main() {
KtClass5("test").content.length // 会崩溃
}
五、Kotlin语言特点
类的继承与重载的 open 关键词
// Kotlin所有的类,默认是final修饰的,不能被继承,与Java相反
// open:移除final修饰
open class Person(private val name: String) {
private fun showName() = "父类的姓名是$name"
// Kotlin所有的函数,默认是final修饰的,不能被重写,与Java相反
open fun myPrint() = println(showName())
}
class Studend(private val subName: String): Person(subName) {
private fun showName() = "子类的姓名是$subName"
override fun myPrint() = println(showName())
}
fun main() {
val person: Person = Studend("zhangsan")
person.myPrint()
}
类型转换
is
as
open class Person(private val name: String) {
fun showName() = "父类的姓名是$name"
// Kotlin所有的函数,默认是final修饰的,不能被重写,与Java相反
open fun myPrint() = println(showName())
}
class Student(private val subName: String): Person(subName) {
private fun showName2() = "子类的姓名是$subName"
override fun myPrint() = println(showName2())
}
fun main() {
val person: Person = Student("zhangsan")
person.myPrint()
println(person is Student) // true
println(person is Person) // true
// is + as
if (person is Student) {
(person as Student).myPrint()
}
if (person is Person) {
println((person as Person).showName())
}
}
智能类型转换
在语句中使用 as 对对象obj进行类型转换后,后就面的语句会自动判断obj的类型为转换后的类型
open class Person(private val name: String) {
fun showName() = "父类的姓名是$name"
// Kotlin所有的函数,默认是final修饰的,不能被重写,与Java相反
open fun myPrint() = println(showName())
fun methodPerson() = println("父类的方法")
}
class Student(private val subName: String): Person(subName) {
private fun showName2() = "子类的姓名是$subName"
override fun myPrint() = println(showName2())
fun methodStudent() = println("子类的方法")
}
fun main() {
val person: Person = Student("zhangsan")
// 智能类型转换:这里调用了 as 转换,下面的语句中的person会自动判断为Student类型
(person as Student).methodStudent()
person.methodStudent() // 不需要再as进行转换
}
超类 Any
在Kotlin中所有的类都隐式继承了 Any,不需要写
Any 类在Kotlin的设计中,只提供标准,看不到实现,实现在各个平台处理好了
相当于Java的Object,但是比Object实现的更高级
class Obj1: Any()
fun main() {
println(Obj1().toString())
}
对象声明
object 类名即是类名,又是类的单例,只有一个创建,是典型的单例
// object KtObject 即是类的实例,又是类名
// 只有一个创建这是典型的单例
object KtObject {
init {
println("KtObject init")
}
fun show() = println("我是show函数")
}
fun main() {
println(KtObject) // 三个打印一致
println(KtObject)
println(KtObject)
KtObject.show()
}
对象表达式
匿名对象表达式:objct: 类名()
具名对象是常见的类声明及对象初始化
对于Java接口,有两种实现方式:object: 对象表达式 和 简洁版
对于Kotlin接口,只有一种实现方式:object: 对象表达式
interface RunnableKt {
fun run()
}
open class KtBaseClass {
open fun add(info: String) = println("KtBaseClass add:$info")
open fun del(info: String) = println("KtBaseClass del:$info")
}
fun main() {
// 匿名对象 表达式
val p = object: KtBaseClass() {
override fun add(info: String) {
println("匿名对象add: $info")
}
override fun del(info: String) {
println("匿名对象del: $info")
}
}
p.add("zhangsan")
p.del("zhangsan")
// 具名方式实现
val p2 = KtBaseClassImpl()
p2.add("zhangsan")
p2.del("zhangsan")
// 对Java的接口,用Kotlin对象表达式实现
val p3 = object: Runnable {
override fun run() {
println("Runnable run ...")
}
}
p3.run()
// 对Java接口,用Java最简介方式实现
val p4 = Runnable {
fun run() {
println("Runnable run2 ...")
}
}
p4.run()
// 对Kotlin的接口,用Kotlin对象表达式实现
object: RunnableKt {
override fun run() {
println("RunnableKt run ...")
}
}.run()
// 对Kotlin接口,用Java最简介方式实现,不能这样用
// RunnableKt {
//
// }
}
class KtBaseClassImpl: KtBaseClass() {
override fun add(info: String) {
println("具名对象add: $info")
}
override fun del(info: String) {
println("具名对象del: $info")
}
}
伴生对象
由来:Kotlin中没有Java的static,伴生对象类似于Java的static
无论类的对象构建多少次,其中的伴生对象只初始化一次
无论伴生对象中的成员被调用多少次,伴生对象只初始化一次
class KtCompanion {
// 伴生对象
companion object {
val info = "test info"
fun show() = println("info: $info")
}
}
fun main() {
println(KtCompanion.Companion.info)
KtCompanion.Companion.show()
KtCompanion()
KtCompanion()
}
内部类
inner 修饰
内部类 能访问 外部类(加inner修饰),外部类 能访问 内部类
嵌套类 不能访问 外部类,外部类 能访问 嵌套类
// 内部类
class Body(_info: String) {
val bodyInfo = _info
fun show() {
Heart().run()
}
inner class Heart { // 默认情况下:内部的类 不能访问 外部的类,内部的类要加修饰符 inner 才能访问外部的类
fun run() = "心脏访问身体信息:$bodyInfo"
}
inner class Hand {
inner class LeftHand {
fun run() = "左手访问身体信息:$bodyInfo"
}
inner class RightHand {
fun run() = "右手访问身体信息:$bodyInfo"
}
}
}
// 嵌套类
class Outer {
val info = "ok"
fun show() {
Nested().output() // 外部类 能访问 嵌套类
}
class Nested {
// fun output() = println("嵌套类: $info") // 嵌套类 不能访问 外部类
fun output() = println("嵌套类")
}
}
fun main() {
// 内部类
Body("ok").Heart().run()
// 嵌套类
Outer.Nested().output()
}
数据类
data 修饰符
// 普通类
class ResponseResultBean1(var code: Int, var msg: String, var data: String)
// 数据类,一般用于 Bean 类,默认提供了 get set 构造函数 解构操作 copy hashCode toString equals
data class ResponseResultBean2(var code: Int, var msg: String, var data: String)
fun main() {
println(ResponseResultBean1(200, "ok", "data"))
println(ResponseResultBean2(200, "ok", "data")) // 打印属性的值
println(
ResponseResultBean1(200, "ok", "data") == ResponseResultBean1(200, "ok", "data")
) // false
println(
ResponseResultBean2(200, "ok", "data") == ResponseResultBean2(200, "ok", "data")
) // true
}
数据类默认的 copy equals hashCode toString 函数
数据类默认的 copy equals hashCode toString 函数只管主构造函数中的属性,不管次构造函数中的属性,使用以上函数时需要注意
data class DataClass(var name: String , var age: Int) {
var coreInfo = ""
constructor(name: String): this(name, 20) {
coreInfo = "核心信息"
}
// 需要重写才有次构造函数中初始化的属性值
override fun toString(): String {
return "toString name: $name, age: $age, coreInfo: $coreInfo"
}
}
fun main() {
val p1 = DataClass("zhangsan")
println(p1)
val p2 = p1.copy("lisi", 23)
println(p2)
}
解构声明
解构必须是按照顺序声明的,且从 component1 开始
class Student(var name: String, var age: Int, var sex: Char) {
operator fun component1() = name
operator fun component2() = age
operator fun component3() = sex
}
fun main() {
val(name, age, sex) = Student("zhangsan", 20, 'm')
println("name: $name, age: $age, sex: $sex")
val(name2, age2, _) = Student("zhangsan", 20, 'm')
println("name: $name2, age: $age2")
}
运算符重载
class AddClass(var number1: Int , var number2: Int) {
operator fun plus(p: AddClass): Int {
return number1 + p.number1 + number2 + p.number2
}
}
fun main() {
val p1 = AddClass(1, 2)
val p2 = AddClass(3, 4)
println(p1 + p2)
}
枚举
enum class
枚举的值等价于枚举本身
enum class Week {
星期一,
星期二,
星期三,
星期四,
星期五,
星期六,
星期日;
}
fun main() {
println(Week.星期一)
// 枚举的值等价于枚举本身
println(Week.星期一 is Week) // true
}
枚举类定义函数
data class LimbsInfo(var name: String, var length: Int) {
fun show() {
println("${name}的长度是$length")
}
}
enum class Limbs(private val limbsInfo: LimbsInfo) {
LEFT_HAND(LimbsInfo("左手", 88)),
RIGHT_HAND(LimbsInfo("右手", 88)),
LEFT_FOOT(LimbsInfo("左脚", 100)),
RIGHT_FOOT(LimbsInfo("右脚", 100));
fun show() = "四肢信息:${limbsInfo.name},长度:${limbsInfo.length}"
// 更新
fun update(limbsInfo: LimbsInfo) {
this.limbsInfo.name = limbsInfo.name
this.limbsInfo.length = limbsInfo.length
}
}
fun main() {
println(Limbs.LEFT_HAND.show())
// 更新
Limbs.LEFT_HAND.update(LimbsInfo("左手", 89))
}
代数数据类型
when 判断枚举值
enum class Exams {
Fraction1,
Fraction2,
Fraction3,
Fraction4
}
class Teacher(private val exam: Exams) {
fun show() =
when (exam) {
Exams.Fraction1 -> "不及格"
Exams.Fraction2 -> "及格"
Exams.Fraction3 -> "优良"
Exams.Fraction4 -> "优秀"
// else -> 上面已经包含全部值,可省略
}
}
fun main() {
println(Teacher(Exams.Fraction4).show())
}
密封类
scaled
成员必须有类型,并且继承本类
sealed class Exams {
object Fraction1: Exams()
object Fraction2: Exams()
object Fraction3: Exams()
class Fraction4(val name: String): Exams();
}
class Teacher(private val exam: Exams) {
fun show() =
when (exam) {
is Exams.Fraction1 -> "不及格"
is Exams.Fraction2 -> "及格"
is Exams.Fraction3 -> "优良"
is Exams.Fraction4 -> "优秀,学生姓名是: ${(this.exam as Exams.Fraction4).name}"
}
}
fun main() {
println(Teacher(Exams.Fraction3).show())
println(Teacher(Exams.Fraction4("zhangsan")).show())
}
数据类使用条件
1. 服务器返回的响应数据 Java Bean 基本可以使用数据类
2. 数据类必须有至少一个参数的主构造函数
3. 数据类必须有参数
4. 数据类不能使用 open abstract inner scaled 等修饰
5. 需要 比较、copy、toString、解构 等丰富功能时,可使用数据类
六、
接口的定义
interface
interface IUSB {
var versionInfo: String
var deviceInsertInfo: String
fun insert(): String
}
class Mouse(override var versionInfo: String = "USB 3.0", override var deviceInsertInfo: String = "鼠标插入USB接口") : IUSB {
override fun insert(): String = "Mouse $versionInfo: $deviceInsertInfo"
}
class Keyboard: IUSB {
override var versionInfo: String = "USB 3.1"
get() = field
set(value) {
field = value
}
override var deviceInsertInfo: String = "键盘插入USB接口"
get() {
println("你get了$field")
return field
}
set(value) {
field = value
println("你set了$value")
}
override fun insert(): String = "Keyboard $versionInfo: $deviceInsertInfo"
}
接口的默认实现
虽然接口成员可以通过 get set 给其赋默认值,但是不建议这样做,因为接口本来就是用来定义标准的
interface IUSB {
// 接口成员不论是val还是var,都不能给其赋值(但有其它方法:get)
// 任何类、接口的val成员是只读的,不能动态改变其值(但是有其它方法:set)
val versionInfo: String
get() = (1..100).shuffled().first().toString()
// set(value) val 不能有set
val deviceInsertInfo: String
get() = "接入设备"
// set(value) val 不能有set
fun insert(): String
}
class Mouse() : IUSB {
override val versionInfo: String
get() = super.versionInfo
override val deviceInsertInfo: String
get() = super.deviceInsertInfo
override fun insert(): String = "Mouse $versionInfo: $deviceInsertInfo"
}
抽象类
abstract class BaseActivity {
fun onCreate() {
setContentView(getLayoutId())
initView()
initData()
}
private fun setContentView(layoutId: Int) {
println("加载布局:$layoutId")
}
abstract fun getLayoutId(): Int
abstract fun initView()
abstract fun initData()
}
class MainActivity: BaseActivity() {
override fun getLayoutId(): Int = 123
override fun initView() {
println("init view")
}
override fun initData() {
println("init data")
}
fun show() {
super.onCreate()
}
}
fun main() {
MainActivity().show()
}
泛型类
class KtBaseTClass<T>(private val obj: T) {
fun show() = println("万能输出器:$obj")
}
data class Student2(val name: String, val age: Int, val sex: Char)
fun main() {
val student = Student2("zhangsan", 22, 'm')
KtBaseTClass(student).show()
}
泛型函数
与 let apply also run 等结合使用
class KtClassGetter<T>(private val isOk: Boolean, private val obj: T) {
fun getClass() {
obj.takeIf { isOk }
}
}
泛型类型转换
fun <I, O> map(inputType: I, isMap: Boolean = true, mapAction: (I) -> O) =
if (isMap) mapAction(inputType) else null
fun main() {
val r = map(123) {
it.toString()
}
println(r)
}
泛型类型约束
<T: xxx> 代表只接收 xxx 及其 子类
class KtClassTranslator<T: Student>(private val inputType: T, private val isR: Boolean = true) {
fun getObj() = inputType.takeIf { isR }
}
vararg关键词(动态参数)
class KtVarargClass<T>(vararg objects: T, var isMap: Boolean) {
// out: T 只能被读取
private val objectArray: Array<out T> = objects
fun showObj(index: Int) = objectArray[index].takeIf { isMap }
fun <O> mapObj(index: Int, mapAction: (T?) -> O) = mapAction(objectArray[index].takeIf { isMap })
}
fun main() {
// 由于传递的动态参数中,包含多种类型,所以泛型真正的类型是 KtVarargClass<{Comparable<*> & java.io.Serializable}>
// 但是不允许这样写,所以使用 Any? 代替
val p: KtVarargClass<Any?> = KtVarargClass("zhangsan", 1234, 5678.9, null, false, isMap = true)
println(p.showObj(0))
println(p.showObj(3))
// map
// mapAction中声明T为可空的(?),所以it也是可空的(?)
val r = p.mapObj(1) {
"转化为String: $it"
}
println(r)
val p2 = KtVarargClass("zhangsan", "lisi", "wangwu", isMap = true)
println(p2.showObj(0))
// p2传递的动态参数中,只包含String类型,所以it的类型为String?
val r2 = p2.mapObj(1) {
it?.length
}
println(r2)
}
[ ]操作符
class KtGetOperator<T>(vararg val objects: T, var isR: Boolean = true) {
// []运算符重载
operator fun get(index: Int): T? {
return objects[index].takeIf { isR }
}
}
fun main() {
val p = KtGetOperator("zhangsan", "lisi", null)
// 只要有一个元素为null,那么所有的元素都是 String?
val r: String? = p.get(1)
println(p.get(0))
println(p.get(2))
}
out 协变
在父类泛型声明处,可以接受子类
// 生产者 out T 协变,T只能被读取,不能修改
interface Producer<out T> {
// 编译错误:T不能被修改
// fun consumer(item: T)
fun product(): T
}
// 消费者 in T 逆变,T不能被读取,只能修改
interface Consumer<in T> {
// 编译错误:T不能被修改
fun consumer(item: T)
// 编译错误:T不能被读取
// fun product(): T
}
// 生产者与消费者 T 默认情况下是不变,T能被修改,也能读取
interface ProducerAndConsumer<T> {
fun consumer(item: T)
fun product(): T
}
open class Animal
open class Human: Animal()
open class Man: Human()
open class Woman: Human()
class ProducerClass1: Producer<Animal> {
override fun product(): Animal {
println("生产者 Animal")
return Animal()
}
}
class ProducerClass2: Producer<Human> {
override fun product(): Human {
println("生产者 Human")
return Human()
}
}
class ProducerClass3: Producer<Man> {
override fun product(): Man {
println("生产者 Man")
return Man()
}
}
class ProducerClass4: Producer<Woman> {
override fun product(): Woman {
println("生产者 Woman")
return Woman()
}
}
fun main() {
val p1: Producer<Animal> = ProducerClass1()
// ProducerClass2声明的泛型为Human,p2声明为Animal不报错,是因为泛型声明为 out,如果非 out 会报错
// out 相当于java中的 ? extends T,
// 例如:List<? extends CharSequence> list = new ArrayList<String>()
val p2: Producer<Animal> = ProducerClass2()
val p3: Producer<Animal> = ProducerClass3()
val p4: Producer<Animal> = ProducerClass4()
}
in 和 out
使用场景:
in 逆变:只能修改,不能读取
out 协变:只能读取,不能修改
class SetClass<in T> {
fun set1(item: T) {
}
}
class GetClass<out T>(_item: T) {
val item = _item
fun get1(): T {
return item
}
}
refield 关键词
class ObjectClass1(val name:String, val age: Int, val sex: Char)
class ObjectClass2(val name:String, val age: Int, val sex: Char)
class ObjectClass3(val name:String, val age: Int, val sex: Char)
class RandomClass {
inline fun <reified T> randomOrDefault(defaultAction: () -> T): T {
val objList = listOf(
ObjectClass1("zhangsan", 22, 'm'),
ObjectClass2("lisi", 23, 'm'),
ObjectClass3("wangwu", 24, 'm'),
)
val randomObj = objList.shuffled().first()
println("产生的随机对象:$randomObj")
// 如果不声明 reified T,it as T 会报错
// 注意 as T?,当takeIf条件不成立时,null as T 会导致崩溃,所以要 T?
return randomObj.takeIf { it is T } as T? ?: defaultAction()
}
}
fun main() {
val result = RandomClass().randomOrDefault<ObjectClass1> {
println("使用默认值")
ObjectClass1("zhangsan", 22, 'm')
}
println("结果:$result")
}
定义扩展函数
类名.扩展函数名()
class KtExtensionBaseClass(val name: String, val age: Int, val sex: Char)
// 扩展函数,会自动生成this引用
fun KtExtensionBaseClass.show() = println("显示信息:name:$name, age:$age, sex:$sex")
// 其它框架中的类也可以添加扩展函数
fun String.addAction(count: Int) = this + "@".repeat(count)
fun main() {
KtExtensionBaseClass("zhangsan", 22, 'm').show()
println("lisi".addAction(3))
}
在超类上定义扩展函数
1. 自己定义的扩展函数不能重复
2. Kotlin内置的扩展函数,我们可以重新定义进行覆盖
fun Any.showContent() = println("内容是:$this")
fun Any.showContent2(): Any {
println("内容2是:$this")
return this
}
fun File.readLine() {
println("覆盖系统的扩展函数")
}
data class ResponseResult(val msg: String, val code: Int)
fun main() {
ResponseResult("success", 200).showContent()
"zhangsan".showContent2().showContent()
File("").readLines()
}
泛型扩展函数
fun <T> T.showContentInfo() = println("${if (this is String) "字符串长度是:$length" else "不是字符串,内容是:$this"}")
fun commonFun() {}
fun main() {
"test".showContentInfo()
val p = 123
p.showContentInfo()
commonFun().showContentInfo()
}
标准函数与泛型扩展函数
private inline fun <I, O> I.mLet(lambda: (I) -> O) = lambda(this)
fun main() {
val r = "test1".mLet {
it.length
}
println(r)
}
val 类名.属性名
get() = "xxx"
扩展属性
val String.myInfo
get() = "zhangsan"
fun main() {
println("".myInfo)
}
可空类型的扩展函数
类名?.扩展函数名()
fun String?.outputValue(defaultValue: String) = println(this ?: defaultValue)
fun main() {
val nullValue: String? = null
nullValue.outputValue("默认值1")
val value = "zhangsan"
value.outputValue("默认值2")
}
infix 关键词
infix:中缀表达式,可以简化代码
1. 需要与扩展函数一起使用
2. 需要在函数的参数中传递一个参数
private infix fun <C1, C2> C1.gogo(c2: C2) {
println("中缀表达式:第一个参数:$this")
println("中缀表达式:第二个参数:$c2")
}
fun main() {
// Kotlin自带,
// 例如:public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
mapOf("零".to(0))
mapOf("一" to 1)
mapOf("二" to 2)
// 自定义
"张三".gogo("李四")
"wangwu" gogo 1
}
定义扩展文件
在实际中比较有用,可以把很多扩展操作定义在一个文件中,使用的时候引入,方便管理
KtExtension.kt 中定义
package com.demo.lib.ext
fun <E> Iterable<E>.randomItemValue() = this.shuffled().first()
MyClass.kt 中使用
import com.demo.lib.ext.randomItemValue
fun main() {
val p = listOf("zhangsan", "lisi", "wangwu").randomItemValue()
println(p)
}
重命名扩展
import xxx.xxx.xxx as xxx
import com.demo.lib.ext.randomItemValue as f
fun main() {
val p = listOf("zhangsan", "lisi", "wangwu").f()
println(p)
}
apply 函数详解
// INPUT.myApply,泛型扩展函数: 让所有的类型,都可以 xxx.myApply 调用
// INPUT.():让匿名函数持有 this
private inline fun <INPUT> INPUT.myApply(lambda: INPUT.() -> Unit): INPUT {
lambda()
return this
}
fun main() {
File("xxx")
.myApply {
setReadable(true)
}
.myApply {
setWritable(true)
}
}
DSL (Domain Specified Language),领域专用语言
// apply5函数,就是DSL编程范式,定义输入输出等规则。
// 1.定义lambda规则标准,输入 必须是Context类,才能调用apply5函数
// 2.定义lambda规则标准,输出 始终返回Context本身
inline fun Context.apply5(lambda: Context.(String) -> Unit): Context {
lambda(info)
return this
}
inline fun File.applyFile(action: (String, String?) -> Unit): File {
action(name, readLines()[0])
return this
}
fun main() {
val context = Context().apply5 {
// 同时持有this和it
toast("success")
// it == String
toast(it)
toast(name)
}
println(context)
File("xxx")
.applyFile { fileName, content ->
println("文件名:$fileName, 内容:$content")
}
.applyFile { fileName, content ->
}
}
变换函数-map
map:生成一个新的集合,新集合中每个元素根据lambda表达式进行转换
fun main() {
val list = listOf("zhansan", "lisi", "wangwu")
list
.map {
"姓名:$it"
}
.map {
println("$it")
}
}
变换函数-flatMap
flatMap:生成一个新的集合,新集合中每个元素都是集合,新集合中每个元素根据lambda表达式进行转换生成
fun main() {
val list = listOf("zhansan", "lisi", "wangwu")
// flatMap返回的是 List<List<String>>,但最终会进行平铺返回 List<String>
val newList: List<String> = list.flatMap {
listOf("$it 在学习Java", "$it 在学习Kotlin")
}
println(newList)
}
// [zhansan 在学习Java, zhansan 在学习Kotlin, lisi 在学习Java, lisi 在学习Kotlin, wangwu 在学习Java, wangwu 在学习Kotlin]
过滤函数-filter
filter:生成一个新的集合,新集合中每个元素符合给定的规则
fun main() {
val list = listOf(
listOf("zhangsan", "lisi"),
listOf("wangwu", "zhaoliu")
)
list.map {
it -> it.filter {
it.contains('z')
}
}.map {
println(it)
// [zhangsan]
// [zhaoliu]
}
list.flatMap {
it -> it.filter {
it.contains('z')
}
}.map {
println(it)
// zhangsan
// zhaoliu
}
}
合并函数-zip
zip:根据两个集合中的元素生成一个新的集合,与原集合index一致,并且新集合元素数量为原两个集合数量最小值
fun main() {
val nameList = listOf("zhangsan", "lisi", "wangwu")
val ageList = listOf(22, 23)
val newList = nameList.zip(ageList)
println(newList)
// [(zhangsan, 22), (lisi, 23)]
newList.forEach {
println("nane: ${it.first}, age: ${it.second}")
}
// nane: zhangsan, age: 22
// nane: lisi, age: 23
}
函数式编程
简化代码,例如 zip 函数的 Java 实现远比 Kotlin 实现所用代码多很多
Kotlin与Java互操作性和可空性
Kotlin调用Java代码时,Java给Kotlin的值,都是类似于 String!,
所以kotlin在接受Java给的值时,直接把类型声明为可空?,避免代码出错
fun main() {
// 错误
println(JavaClass().info1.length)
println(JavaClass().info2.length) // 崩溃
// 错误
val info1 = JavaClass().info1
val info2 = JavaClass().info2
println(info1.length)
println(info2.length) // 崩溃
// 正确
// Kotlin调用Java代码时,Java给Kotlin的值,都是类似于 String!
val info11 = JavaClass().info1
val info21 = JavaClass().info2
println(info11?.length)
println(info21?.length)
// 正确,推荐
// Kotlin调用Java代码时,Java给Kotlin的值,都是类似于 String!
// 所以kotlin在接受Java给的值时,直接把类型声明为可空?,避免代码出错
val info12: String? = JavaClass().info1
val info22: String? = JavaClass().info2
println(info12?.length)
println(info22?.length)
}
单例模式
1. 饿汉式
object SigletonDemo
2. 懒汉式
class SingletonDemo private constructor() {
companion object {
private var instance: SingletonDemo? = null
get() {
if (field == null) {
field = SingletonDemo()
}
return field
}
fun getInstanceAction(): SingletonDemo = instance !!
}
fun show() = println("show")
}
fun main() {
SingletonDemo.getInstanceAction().show()
}
3. 懒汉式+线程安全:@Synchronized
class SingletonDemo private constructor() {
companion object {
private var instance: SingletonDemo? = null
get() {
if (field == null) {
field = SingletonDemo()
}
return field
}
@Synchronized
fun getInstanceAction(): SingletonDemo = instance !!
}
fun show() = println("show")
}
fun main() {
SingletonDemo.getInstanceAction().show()
}
4. 懒汉式+双重校验安全:by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { ... }
class SingletonDemo private constructor() {
companion object {
val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { SingletonDemo() }
}
fun show() = println("show")
}
fun main() {
SingletonDemo.instance.show()
}
注解 @JvmName 与 Kotlin
@JvmName 用于Kotlin文件,在Kotlin文件自动生成Java类时,指定类名称
@file:JvmName("指定的类名")
KtBase.kt
@file:JvmName("Stu")
package com.demo.lib
fun showStudentInfo(info: String) = println(info)
JavaCallKotlin.java
public class JavaCallKotlin {
public static void main(String[] args) {
// KtBaseKt.showStudentInfo("info");
Stu.showStudentInfo("info");
}
}
注解 @JvmField 与 Kotlin
在Kotlin类的属性上增加@JvmField,会剔除自动生成的getXxx()方法,在Java调用该属性时可直接调用
Persons.kt
class Persons {
@JvmField
val names = listOf("zhangsan", "lisi", "wangwu")
}
JavaCallKotlin.java
public class JavaCallKotlin {
public static void main(String[] args) {
Persons persons = new Persons();
// 如果不加 @JvmField,只能调用getXxx()方法获取属性
for (String name : persons.getNames()) {
System.out.println(name);
}
// 加了 @JvmField,可以直接调用属性,不会再生成getXxx()方法
for (String name : persons.names) {
System.out.println(name);
}
}
}
注解 @JvmOverloads 与 Kotlin
@JvmOverloads 用于Kotlin函数,在Java调用Kotlin函数时,可以使用其定义好的参数默认值
原理是编译器会生成多个传递不同参数的重载方法,给Java用
KtBase.kt
fun show(name: String, age: Int = 20, sex: Char = 'm') {
println("name: $name, age: $age, sex: $sex")
}
@JvmOverloads
fun toast(name: String, sex: Char = 'm') {
println("name: $name, sex: $sex")
}
JavaCallKotlin.java
public class JavaCallKotlin {
public static void main(String[] args) {
// KtBaseKt.show("zhangsan"); // Java不能享用Kotlin函数的参数默认值
KtBaseKt.toast("zhangsan"); // Kotlin函数加上@JvmOverloads后,Java可以享用参数默认值
}
}
注解 @JvmStatic 与 Kotlin
MyObject.kt
class MyObject {
companion object {
@JvmField // 编译时会把TARGET移出Companion内部类
val TARGET = "公园"
@JvmStatic // 编译时会把showAction方法移出Companion内部类
fun showAction(name: String) = println("${name}要去${TARGET}玩")
}
}
JavaCallKotlin.java
public class JavaCallKotlin {
public static void main(String[] args) {
// System.out.println(MyObject.Companion.getTARGET()); // 不加@JvmField,只能通过.Companion调用
MyObject.Companion.showAction("zhangsan"); // 不加@JvmStatic,只能通过.Companion调用
System.out.println(MyObject.TARGET); // 加上@JvmField,可以直接调用,并且会剔除getXxx()方法
MyObject.showAction("zhangsan"); // 加上@JvmStatic,可以直接调用
}
}
手写事件变换操作符-create
fun main() {
create {
"zhangsan"
}
}
class RxCoreClass() {
}
inline fun <OUTPUT> create(action: () -> OUTPUT): RxCoreClass {
}
手写事件变换操作符-中转站
fun main() {
create {
"zhangsan"
}
}
class RxCoreClass<T>(valueItem: T) {
// create 操作符,最后一行的返回值,流向此处
}
inline fun <OUTPUT> create(action: () -> OUTPUT) = RxCoreClass(action())
手写事件变换操作符-map
fun main() {
create {
"zhangsan"
}.map {
}.map {
}
}
// valueItem == create 操作符 最后一行的返回值,流向此处
class RxCoreClass<T>(var valueItem: T)
inline fun <I, O> RxCoreClass<I>.map(mapAction: I.() -> O): RxCoreClass<O> {
return RxCoreClass(mapAction(valueItem))
}
inline fun <OUTPUT> create(action: () -> OUTPUT) = RxCoreClass(action())
手写事件变换操作符-observer
fun main() {
create {
123
}.map {
"值是:$this"
}.map {
"长度是:$length"
}.observer {
println(this)
}
}
// valueItem == create 操作符 最后一行的返回值,流向此处
class RxCoreClass<T>(var valueItem: T)
inline fun <I, O> RxCoreClass<I>.map(mapAction: I.() -> O): RxCoreClass<O> = RxCoreClass(mapAction(valueItem))
inline fun <I> RxCoreClass<I>.observer(observerAction: I.() -> Unit) = observerAction(valueItem)
inline fun <OUTPUT> create(action: () -> OUTPUT) = RxCoreClass(action())