Attention:
- 本文章適用于有編程基礎(chǔ)的人石洗,如果有多種編程語(yǔ)言基礎(chǔ),例如Swift紧显,會(huì)發(fā)現(xiàn)很多相似之處讲衫,學(xué)習(xí)起來(lái)也會(huì)比較容易
- 本文章屬于筆記,沒(méi)有知識(shí)講解條理孵班,不太適合透徹學(xué)習(xí)Kotlin涉兽,純屬寫代碼時(shí)的參考
一、基礎(chǔ)
基本內(nèi)置數(shù)據(jù)類型
- String
- Char
- Boolean --> Java boolean
- Int --> Java int
- Double --> Java double
- Float --> Java float
- List
- Set
- Map
Kotlin中一些引用類型編譯器會(huì)轉(zhuǎn)化為Java的基本類型篙程,不會(huì)有額外的性能消耗
編譯時(shí)常量
const val PI = 3.1415
- 編譯時(shí)常量只能是常用的基本數(shù)據(jù)類型:(String, Double, Int, Float, Long, Short, Byte, Char, Boolean)
- 編譯時(shí)常量只能定義在函數(shù)外(如果在函數(shù)內(nèi)定義的話就必須在運(yùn)行時(shí)才能調(diào)用函數(shù)賦值枷畏,就不能稱是編譯時(shí)常量了,所以編譯時(shí)常量只能定義在函數(shù)之外虱饿,這樣就能在編譯期間就初始化了)
range表達(dá)式
when表達(dá)式
表達(dá)式有返回值拥诡,語(yǔ)句沒(méi)有返回值
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 個(gè)小時(shí)") // $變量名 后面沒(méi)有字符串(標(biāo)點(diǎn)符號(hào)、空格可以)的可以不加{}
val isLogin = true
println("${if (isLogin) "登錄成功" else "登錄失敗氮发,請(qǐng)重試"}")
函數(shù)
函數(shù)參數(shù)的默認(rèn)值
fun action1(name: String, age: Int) {
println("姓名渴肉;$name, 年齡:$age")
}
fun action2(name: String, age: Int = 88) {
println("姓名;$name, 年齡:$age")
}
具名函數(shù)參數(shù)
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函數(shù)特點(diǎn)
Nothing類型特點(diǎn)
TODO() // 終止程序繼續(xù)執(zhí)行爽冕,拋出一個(gè)異常
fun main() {
show(-1)
}
private fun show(number: Int) {
when(number) {
-1 -> TODO("分?jǐn)?shù)不合法")
in 0..59 -> println("不及格")
in 60..70 -> println("及格")
in 71..100 -> println("優(yōu)秀")
}
}
帶反引號(hào)函數(shù)名特點(diǎn)
fun main() {
// 第一種情況:函數(shù)特殊命名
`登錄功能測(cè)試 需求實(shí)現(xiàn)人張三`("test", "pwd")
// 第二種情況:函數(shù)命名使用了Kotlin中的關(guān)鍵詞
JavaTest.`is`()
JavaTest.`in`()
// 第三種情況:函數(shù)名違反命名規(guī)則仇祭,反編譯時(shí)導(dǎo)致崩潰防止反編譯
`655435383`()
}
private fun `655435383`() {
}
// 文檔中注釋
// 防止反編譯的函數(shù)`655435383`,作用是xxx
二颈畸、語(yǔ)法糖
匿名函數(shù)
fun main() {
val len = "test".count()
println(len) // 4
val len2 = "test".count {
it == 't'
}
println(len2) // 2
}
匿名函數(shù)-函數(shù)類型&隱式返回
fun main() {
// 1.函數(shù)輸入輸出的聲明
val methodAction: () -> String
// 2.對(duì)上面聲明函數(shù)的實(shí)現(xiàn)
methodAction = {
val name = "name"
"返回值:$name" //最后一行就是函數(shù)的返回值
}
// 3.調(diào)用
println(methodAction())
}
匿名函數(shù)-函數(shù)參數(shù)
fun main() {
val methodAction: (Int, Int, Int) -> String = { number1, number2, number3 ->
val name = "name"
"$name: $number1, $number2, $number3"
}
println(methodAction(10, 20, 30))
}
匿名函數(shù)-it關(guān)鍵字
匿名函數(shù)的參數(shù)只有一個(gè)的話乌奇,會(huì)自動(dòng)生成參數(shù)名it,代表被傳入的參數(shù)對(duì)象
fun main() {
val methodAction: (String) -> String = {
"$it"
}
println(methodAction("name"))
val methodAction2: (Double) -> String = {
"$it"
}
println(methodAction2(123.4))
}
匿名函數(shù)-類型推斷
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))
}
在函數(shù)中定義參數(shù)是函數(shù)的參數(shù)
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
}
簡(jiǎn)略寫法
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")
})
// 第三種方式眯娱,簡(jiǎn)略寫法
loginApi("name", "pwdd") { msg: String, code: Int ->
println("msg: $msg, code: $code")
}
}
內(nèi)聯(lián)函數(shù)
inline
如果函數(shù)使用lambda做為參數(shù)礁苗,需要聲明成內(nèi)聯(lián),否則在調(diào)用時(shí)會(huì)生成多個(gè)對(duì)象來(lái)完成lambda的調(diào)用困乒,引起性能損耗寂屏。
如果函數(shù)使用內(nèi)聯(lián),相當(dāng)于C++的#define 宏定義 宏替換,會(huì)把代碼替換到調(diào)用處迁霎,沒(méi)有任何新建函數(shù)吱抚、新建對(duì)象的損耗
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
}
函數(shù)引用
lambda屬于函數(shù)類型的 對(duì)象,在使用函數(shù)作為參數(shù)時(shí)考廉,需要把普通函數(shù)通過(guò) :: 轉(zhuǎn)為函數(shù)類型的對(duì)象
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("結(jié)果: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)
}
}
使用函數(shù)做為返回類型
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 ->
"我是匿名函數(shù):name: $name, age: $age"
}
}
匿名函數(shù)與具名函數(shù)
fun main() {
// 匿名函數(shù)
showPersonInfo("張三", 30) {
println("$it")
}
// 具名函數(shù)
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內(nèi)置函數(shù)
Kotlin可空性特點(diǎn)
?
安全調(diào)用操作符
?
帶let的安全調(diào)用
fun main() {
var name: String? = null
// name = "張三"
val result = name?.let {
// it == name 本身
// 如果能執(zhí)行到這里,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("代碼不嚴(yán)謹(jǐn)")
先決條件函數(shù)
checkNotNull()
requireNotNull()
require()
substring
split
fun main() {
val text = "A,B,C,D"
val list = text.split(",")
// 直接輸出
println(list)
// 解構(gòu)昌粤,C++中也有解構(gòu)
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)
}
== 與 === 比較操作
== 值比較
=== 引用比較
數(shù)字類型的安全轉(zhuǎn)換函數(shù)
toXxxOrNull()
fun main() {
val number = "888.8".toIntOrNull()
println(number ?: "轉(zhuǎn)換錯(cuò)誤")
}
Double 轉(zhuǎn) Int
fun main() {
println(123.4567.toInt()) // 123, 只保留整數(shù)
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 內(nèi)置函數(shù)
1.匿名函數(shù)中自動(dòng)生成this參數(shù)既绕,代表對(duì)象本身
2.apply函數(shù)始終返回的是對(duì)象本身,一般用于鏈?zhǔn)秸{(diào)用
val file = File("文件路徑")
file.setExecutable(true)
file.setReadable(true)
println(file.readLine())
// 等價(jià)于
val file = File("文件路徑")
file.apply {
// apply函數(shù)默認(rèn)生成一個(gè)代表當(dāng)前對(duì)象的this
setExecutable(true)
}.apply {
setReadable(true)
}.apply {
println(readlnOrNull())
}
let 內(nèi)置函數(shù)
1.匿名函數(shù)中自動(dòng)生成it參數(shù)涮坐,代表對(duì)象本身
2.最后一行做為返回值
fun main() {
val result = listOf(6, 4, 7, 9).let {
// it == 對(duì)象本身
// 最后一行做為返回值
it.first() + it.first()
}
println(result)
val result2 = getMethod(null)
println(result2)
}
fun getMethod(value: String?): String {
return value?.let {
it
} ?: "傳值為null"
}
run 內(nèi)置函數(shù)
1.匿名函數(shù)中自動(dòng)生成this參數(shù)凄贩,代表對(duì)象本身,一般用于值類型發(fā)生改變的鏈?zhǔn)秸{(diào)用
2.最后一行做為返回值
fun main() {
val str = "12345"
// run 與 具名函數(shù)
str
.run(::isLengthOk)
.run(::showText)
.run(::mapText)
.run(::println)
// run 與 匿名函數(shù)
str
.run {
length > 3
}
.run {
if (this) "長(zhǎng)度ok" else "長(zhǎng)度不夠"
}
.run {
"[$this]"
}
.run {
println(this)
}
}
fun isLengthOk(str: String) = str.length > 3
fun showText(isLengthOK: Boolean) = if (isLengthOK) "長(zhǎng)度ok" else "長(zhǎng)度不夠"
fun mapText(showText: String) = "[$showText]"
with 內(nèi)置函數(shù)
1.匿名函數(shù)中自動(dòng)生成this參數(shù)袱讹,代表對(duì)象本身疲扎,一般用于值類型發(fā)生改變的鏈?zhǔn)秸{(diào)用
2.最后一行做為返回值
與 run 函數(shù)基本相同,只是調(diào)用方式不同
fun main() {
val str = "12345"
// with 與 具名函數(shù)
val r1 = with(str, ::isLengthOk)
val r2 = with(r1, :: showText)
val r3 = with(r2, ::mapText)
with(r3, ::println)
// with 與 匿名函數(shù)
val r11 = with(str) {
length > 3
}
val r22 = with(r11) {
if (this) "長(zhǎng)度ok" else "長(zhǎng)度不夠"
}
val r33 = with(r22) {
"[$this]"
}
with(r33) {
println(this)
}
}
fun isLengthOk(str: String) = str.length > 3
fun showText(isLengthOK: Boolean) = if (isLengthOK) "長(zhǎng)度ok" else "長(zhǎng)度不夠"
fun mapText(showText: String) = "[$showText]"
also 內(nèi)置函數(shù)
1.匿名函數(shù)中自動(dòng)生成it參數(shù)捷雕,代表對(duì)象本身
2.also函數(shù)始終返回的是對(duì)象本身椒丧,一般用于鏈?zhǔn)秸{(diào)用
fun main() {
val file = File("文件路徑")
file
.also {
it.setExecutable(true)
it.setReadable(true)
}
.also {
println(it.readLines())
}
}
takeIf 內(nèi)置函數(shù)
如果所傳遞函數(shù)對(duì)象中的條件滿足,就返回自身
一般情況下救巷,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 內(nèi)置函數(shù)
如果所傳遞函數(shù)對(duì)象中的條件不滿足,就返回自身
與 takeIf 功能相反
takeUnless+對(duì)象中的屬性判空一般一起使用浦译,可以驗(yàn)證對(duì)象的屬性有沒(méi)有設(shè)置值
class Manager {
private var info: String? = null
fun getInfo() = info
fun setInfo(info: String) {
this.info = info
}
}
fun main() {
val manager = Manager()
// takeUnless+對(duì)象中的屬性判空一般一起使用棒假,可以驗(yàn)證對(duì)象的屬性有沒(méi)有設(shè)置值
val r = manager.getInfo().takeUnless { it.isNullOrBlank() } ?: "未初始化"
println(r)
}
四、集合與對(duì)象
List
盡量使用 getOrElse 或 getOrNull 獲取數(shù)組中元素
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 // 沒(méi)有這個(gè)函數(shù)
// 不可變 List 轉(zhuǎn) 可變 List
val list3 = list2.toMutableList()
// 可變 List 轉(zhuǎn) 不可變 List
val list4 = list.toList()
}
mutator函數(shù)管怠、removeIf
+= -=淆衷,靠運(yùn)算符重載實(shí)現(xiàn)
removeIf 有條件的移除List中元素,會(huì)自動(dòng)遍歷元素
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")
}
}
解構(gòu)語(yǔ)法過(guò)濾元素
fun main() {
val list = listOf("Zhangsan", "Lisi", "Wangwu")
// 解構(gòu),元素值不可變
val(value1, value2, value3) = list
println("value1: $value1, value2: $value2, value3: $value3")
// 解構(gòu)甚带,元素值可變
var(v1, v2, v3) = list
v1 = "ok"
println("v1: $v1, v2: $v2, v1: $v3")
// 解構(gòu)時(shí)過(guò)濾元素她肯,可以節(jié)省一點(diǎn)性能
val(_, n2, n3) = list
println("n2: $n2, n3: $n3") // 沒(méi)有第一個(gè)
}
Set
不會(huì)出現(xiàn)重復(fù)元素
盡量使用 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"
}
}
集合轉(zhuǎn)換與快捷函數(shù)
fun main() {
val list = listOf("A", "A", "B", "C")
// List 轉(zhuǎn) Set 去重
val set = list.toSet()
println(set)
// List 轉(zhuǎn) Set 轉(zhuǎn) List 去重
val list2 = list.toSet().toList()
println(list2)
// 快捷函數(shù) distinct 去重
val list3 = list.distinct() // 內(nèi)部:轉(zhuǎn) 可變Set 轉(zhuǎn) List
println(list3)
}
數(shù)組
盡量使用elementAtOrElse 或 elementAtOrNull 獲取元素
Kotlin語(yǔ)言中的各種數(shù)組類型,雖然是引用類型鹰贵,背后可以編譯成Java基本數(shù)據(jù)類型
IntArray intArrayOf
DoubleArray doubleArrayOf
LongArray longArrayOf
ShortArray shortArrayOf
ByteArray byteArrayOf
FloatArray floatArrayOf
BooleanArray booleanArrayOf
Array arrayOf 對(duì)象數(shù)組
fun main() {
val intArray = intArrayOf(1, 2, 3)
// 取元素
intArray.elementAt(0)
// intArray.elementAt(3) // 崩潰
intArray.elementAtOrElse(3) { -1 }
intArray.elementAtOrNull(3) ?: "越界"
// List 轉(zhuǎn) 數(shù)組
val charArray = listOf('A', 'B').toCharArray()
// 對(duì)象數(shù)組
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)
// 方式一 []晴氨,等價(jià)于 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碉输,獲取不到會(huì)崩潰
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) // 等價(jià)于 []
// getOrPut 沒(méi)有的話就添加進(jìn)去
map.getOrPut("D") { 4.0 }
println(map["D"])
// getOrPut 有的話就取出來(lái)
map.getOrPut("A") { 4.0 }
// getOrDefault 沒(méi)有就取傳遞的默認(rèn)值
val r = map.getOrDefault("F", 6.0)
println(r)
}
定義類籽前、field關(guān)鍵詞
對(duì)于已經(jīng)賦初始值的屬性,field 代表當(dāng)前屬性的值
class KtBaseClass {
var name = "zhangsan"
// 自動(dòng)生成隱式代碼,寫不寫都有
get() = field
set(value) {
field = value
}
var info = "is ok"
// 重寫隱式代碼枝哄,擴(kuò)展功能
get() = field.capitalize()
set(value) {
field = "**${value}**"
}
}
類的計(jì)算屬性 與 防范競(jìng)態(tài)條件
計(jì)算屬性:使用get函數(shù)覆蓋field的寫法
防范競(jìng)態(tài)條件:當(dāng)被調(diào)用的屬性可能為null時(shí)肄梨,需要使用防空指針的寫法編寫代碼,這種寫法稱為防范競(jìng)態(tài)條件
class KtBaseClass {
val number: Int
// 計(jì)算屬性挠锥,get函數(shù)覆蓋了field众羡,在對(duì)應(yīng)的java代碼中,也不會(huì)生成對(duì)應(yīng)名稱的屬性
get() = (1..1000).shuffled().first()
val info: String? = null
// 防范競(jìng)態(tài)條件蓖租,當(dāng)被調(diào)用的成員可能為null時(shí)粱侣,就必須使用防范競(jìng)態(tài)條件
fun getShowInfo(): String {
// 這種寫法就是防范競(jìng)態(tài)條件,這種寫法會(huì)大量使用
return info?.let {
if (it.isBlank()) {
"info 是 空值"
} else {
"info 結(jié)果:$it"
}
} ?: "info 是 null"
}
}
類的主構(gòu)造函數(shù)
// 主構(gòu)造函數(shù):隱式自動(dòng)生成蓖宦,不需要寫
class KtBaseClass() {
}
// 主構(gòu)造函數(shù):傳入的值命名要按照 _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不可修改稠茂,沒(méi)有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)
}
在類的主構(gòu)造函數(shù)中定義屬性
// 在主構(gòu)造函數(shù)中直接定義屬性
class KtBaseClass(var name: String, val sex: Char, var age: Int) {
fun show() {
println(name)
}
}
類的次構(gòu)造函數(shù)
class KtBaseClass(var name: String) { // 主構(gòu)造
constructor(name: String, sex: Char) : this(name) { // 次構(gòu)造函數(shù)必須調(diào)用主構(gòu)造函數(shù)
println("次構(gòu)造函數(shù):name: $name, sex: $sex")
}
}
fun main() {
val p = KtBaseClass("zhangsan", 'm')
println(p.name)
}
構(gòu)造函數(shù)中參數(shù)的默認(rèn)值
class KtBaseClass(var name: String = "zhangsan") { // 主構(gòu)造
constructor(name: String = "zhangsan", sex: Char = 'm') : this(name) { // 次構(gòu)造函數(shù)必須調(diào)用主構(gòu)造函數(shù)
println("次構(gòu)造函數(shù):name: $name, sex: $sex")
}
}
fun main() {
val p = KtBaseClass("zhangsan", 'm') // 調(diào)用次構(gòu)造函數(shù)
println(p.name)
val p2 = KtBaseClass() // 優(yōu)先調(diào)用主構(gòu)造函數(shù)
}
類的初始化塊
class KtBaseClass(_name: String) { // 主構(gòu)造
// 相當(dāng)于Java的 {} 構(gòu)造代碼塊柠偶,在主構(gòu)造函數(shù)執(zhí)行時(shí)執(zhí)行
// 可以使用構(gòu)造函數(shù)傳入的變量
init {
println("主構(gòu)造函數(shù)調(diào)用:name: $_name")
// 可以在這里判斷傳入值的合法性,驗(yàn)證不通過(guò)會(huì)拋出異常
require(_name.isNotBlank()) {
"name不能是空值"
}
}
constructor(name: String, sex: Char) : this(name) { // 次構(gòu)造函數(shù)必須調(diào)用主構(gòu)造函數(shù)
println("次構(gòu)造函數(shù):name: $name, sex: $sex")
}
}
fun main() {
KtBaseClass("zhangsan", 'm') // 調(diào)用次構(gòu)造函數(shù)
}
構(gòu)造初始化順序
// 第一步:生成 val sex
class KtBaseClass(_name: String, val sex: Char) { // 主構(gòu)造
// 第二步:生成 val name主慰。與init代碼塊平級(jí)嚣州,寫在init代碼塊前所以先執(zhí)行
val name = _name
init {
val nameValue = _name // 第三步:生成 val nameValue
println("init代碼塊調(diào)用:name: $_name")
}
constructor(name: String, sex: Char, age: Int) : this(name, sex) { // 次構(gòu)造函數(shù)必須調(diào)用主構(gòu)造函數(shù)
// 第五步:執(zhí)行次構(gòu)造函數(shù)
println("次構(gòu)造函數(shù):name: $name, sex: $sex, age: $age")
}
// 第四步:生成 val info
val info = "test info"
}
fun main() {
KtBaseClass("zhangsan", 'm') // 調(diào)用次構(gòu)造函數(shù)
}
延遲初始化 lazyinit
使用 lazyinit 的屬性前需要手動(dòng)初始化
class KtClass {
lateinit var info: String
fun loadInfo() {
info = "初始化成功"
}
fun showInfo() {
// if (info == null) {} // 不能這樣用,在info初始化前任何使用都會(huì)崩潰
if (::info.isInitialized) {
println("info: $info")
} else {
println("info還沒(méi)初始化")
}
}
}
fun main() {
val p = KtClass()
// println(p.info) // lazyinit的屬性在初始化前使用會(huì)導(dǎo)致崩潰
// p.loadInfo()
// println(p.info)
p.showInfo()
}
惰性初始化 by lazy
屬性在使用時(shí)自動(dòng)初始化
class KtClass {
// 普通方式(餓漢式)
val info1 = readFromDB1()
val info2 by lazy { readFromDB2() }
fun readFromDB1(): String {
println("1開始讀取數(shù)據(jù)")
println("1讀取數(shù)據(jù)中...")
println("1讀取數(shù)據(jù)中...")
println("1讀取數(shù)據(jù)中...")
println("1讀取數(shù)據(jù)中...")
println("1結(jié)束讀取數(shù)據(jù)")
return "info1 load success"
}
fun readFromDB2(): String {
println("2開始讀取數(shù)據(jù)")
println("2讀取數(shù)據(jù)中...")
println("2讀取數(shù)據(jù)中...")
println("2讀取數(shù)據(jù)中...")
println("2讀取數(shù)據(jù)中...")
println("2結(jié)束讀取數(shù)據(jù)")
return "info2 load success"
}
}
fun main() {
val p = KtClass()
Thread.sleep(5000L)
println("即將開始使用info1")
println("讀取到的數(shù)據(jù):info: ${p.info1}")
println()
println("即將開始使用info2")
println("讀取到的數(shù)據(jù):info: ${p.info2}")
}
// 輸出
1開始讀取數(shù)據(jù)
1讀取數(shù)據(jù)中...
1讀取數(shù)據(jù)中...
1讀取數(shù)據(jù)中...
1讀取數(shù)據(jù)中...
1結(jié)束讀取數(shù)據(jù)
即將開始使用info1
讀取到的數(shù)據(jù):info: info1 load success
即將開始使用info2
2開始讀取數(shù)據(jù)
2讀取數(shù)據(jù)中...
2讀取數(shù)據(jù)中...
2讀取數(shù)據(jù)中...
2讀取數(shù)據(jù)中...
2結(jié)束讀取數(shù)據(jù)
讀取到的數(shù)據(jù):info: info2 load success
初始化陷阱
注意屬性與init代碼塊定義代碼順序
class KtClass2 {
init {
// 與 var name 生成處于同一優(yōu)先級(jí)共螺,執(zhí)行順序與定義順序有關(guān)该肴,所以不能這樣用,必須先定義 val number
number = number.times(9)
}
var number = 9
}
初始化陷阱2
class KtClass3 {
var info: String
init {
getInfoMethod() // info 還沒(méi)初始化就調(diào)用了
info = "test info" // 需要把這行放在上一行上面
}
fun getInfoMethod() {
println("info: ${info[0]}")
}
}
fun main() {
KtClass3() // 會(huì)崩潰
}
初始化陷阱3
class KtClass5(_info: String) {
var content: String = getInfoContent()
var info = _info // 需要把這行放到最前面
fun getInfoContent() = info
}
fun main() {
KtClass5("test").content.length // 會(huì)崩潰
}
五藐不、Kotlin語(yǔ)言特點(diǎn)
類的繼承與重載的 open 關(guān)鍵詞
// Kotlin所有的類匀哄,默認(rèn)是final修飾的,不能被繼承雏蛮,與Java相反
// open:移除final修飾
open class Person(private val name: String) {
private fun showName() = "父類的姓名是$name"
// Kotlin所有的函數(shù)涎嚼,默認(rèn)是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()
}
類型轉(zhuǎn)換
is
as
open class Person(private val name: String) {
fun showName() = "父類的姓名是$name"
// Kotlin所有的函數(shù)法梯,默認(rèn)是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())
}
}
智能類型轉(zhuǎn)換
在語(yǔ)句中使用 as 對(duì)對(duì)象obj進(jìn)行類型轉(zhuǎn)換后立哑,后就面的語(yǔ)句會(huì)自動(dòng)判斷obj的類型為轉(zhuǎn)換后的類型
open class Person(private val name: String) {
fun showName() = "父類的姓名是$name"
// Kotlin所有的函數(shù),默認(rèn)是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")
// 智能類型轉(zhuǎn)換:這里調(diào)用了 as 轉(zhuǎn)換,下面的語(yǔ)句中的person會(huì)自動(dòng)判斷為Student類型
(person as Student).methodStudent()
person.methodStudent() // 不需要再as進(jìn)行轉(zhuǎn)換
}
超類 Any
在Kotlin中所有的類都隱式繼承了 Any产喉,不需要寫
Any 類在Kotlin的設(shè)計(jì)中捂掰,只提供標(biāo)準(zhǔn)敢会,看不到實(shí)現(xiàn),實(shí)現(xiàn)在各個(gè)平臺(tái)處理好了
相當(dāng)于Java的Object这嚣,但是比Object實(shí)現(xiàn)的更高級(jí)
class Obj1: Any()
fun main() {
println(Obj1().toString())
}
對(duì)象聲明
object 類名即是類名鸥昏,又是類的單例,只有一個(gè)創(chuàng)建疤苹,是典型的單例
// object KtObject 即是類的實(shí)例互广,又是類名
// 只有一個(gè)創(chuàng)建這是典型的單例
object KtObject {
init {
println("KtObject init")
}
fun show() = println("我是show函數(shù)")
}
fun main() {
println(KtObject) // 三個(gè)打印一致
println(KtObject)
println(KtObject)
KtObject.show()
}
對(duì)象表達(dá)式
匿名對(duì)象表達(dá)式:objct: 類名()
具名對(duì)象是常見的類聲明及對(duì)象初始化
對(duì)于Java接口,有兩種實(shí)現(xiàn)方式:object: 對(duì)象表達(dá)式 和 簡(jiǎn)潔版
對(duì)于Kotlin接口卧土,只有一種實(shí)現(xiàn)方式:object: 對(duì)象表達(dá)式
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() {
// 匿名對(duì)象 表達(dá)式
val p = object: KtBaseClass() {
override fun add(info: String) {
println("匿名對(duì)象add: $info")
}
override fun del(info: String) {
println("匿名對(duì)象del: $info")
}
}
p.add("zhangsan")
p.del("zhangsan")
// 具名方式實(shí)現(xiàn)
val p2 = KtBaseClassImpl()
p2.add("zhangsan")
p2.del("zhangsan")
// 對(duì)Java的接口惫皱,用Kotlin對(duì)象表達(dá)式實(shí)現(xiàn)
val p3 = object: Runnable {
override fun run() {
println("Runnable run ...")
}
}
p3.run()
// 對(duì)Java接口,用Java最簡(jiǎn)介方式實(shí)現(xiàn)
val p4 = Runnable {
fun run() {
println("Runnable run2 ...")
}
}
p4.run()
// 對(duì)Kotlin的接口尤莺,用Kotlin對(duì)象表達(dá)式實(shí)現(xiàn)
object: RunnableKt {
override fun run() {
println("RunnableKt run ...")
}
}.run()
// 對(duì)Kotlin接口旅敷,用Java最簡(jiǎn)介方式實(shí)現(xiàn),不能這樣用
// RunnableKt {
//
// }
}
class KtBaseClassImpl: KtBaseClass() {
override fun add(info: String) {
println("具名對(duì)象add: $info")
}
override fun del(info: String) {
println("具名對(duì)象del: $info")
}
}
伴生對(duì)象
由來(lái):Kotlin中沒(méi)有Java的static颤霎,伴生對(duì)象類似于Java的static
無(wú)論類的對(duì)象構(gòu)建多少次媳谁,其中的伴生對(duì)象只初始化一次
無(wú)論伴生對(duì)象中的成員被調(diào)用多少次,伴生對(duì)象只初始化一次
class KtCompanion {
// 伴生對(duì)象
companion object {
val info = "test info"
fun show() = println("info: $info")
}
}
fun main() {
println(KtCompanion.Companion.info)
KtCompanion.Companion.show()
KtCompanion()
KtCompanion()
}
內(nèi)部類
inner 修飾
內(nèi)部類 能訪問(wèn) 外部類(加inner修飾)友酱,外部類 能訪問(wèn) 內(nèi)部類
嵌套類 不能訪問(wèn) 外部類晴音,外部類 能訪問(wèn) 嵌套類
// 內(nèi)部類
class Body(_info: String) {
val bodyInfo = _info
fun show() {
Heart().run()
}
inner class Heart { // 默認(rèn)情況下:內(nèi)部的類 不能訪問(wèn) 外部的類,內(nèi)部的類要加修飾符 inner 才能訪問(wèn)外部的類
fun run() = "心臟訪問(wèn)身體信息:$bodyInfo"
}
inner class Hand {
inner class LeftHand {
fun run() = "左手訪問(wèn)身體信息:$bodyInfo"
}
inner class RightHand {
fun run() = "右手訪問(wèn)身體信息:$bodyInfo"
}
}
}
// 嵌套類
class Outer {
val info = "ok"
fun show() {
Nested().output() // 外部類 能訪問(wèn) 嵌套類
}
class Nested {
// fun output() = println("嵌套類: $info") // 嵌套類 不能訪問(wèn) 外部類
fun output() = println("嵌套類")
}
}
fun main() {
// 內(nèi)部類
Body("ok").Heart().run()
// 嵌套類
Outer.Nested().output()
}
數(shù)據(jù)類
data 修飾符
// 普通類
class ResponseResultBean1(var code: Int, var msg: String, var data: String)
// 數(shù)據(jù)類缔杉,一般用于 Bean 類锤躁,默認(rèn)提供了 get set 構(gòu)造函數(shù) 解構(gòu)操作 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
}
數(shù)據(jù)類默認(rèn)的 copy equals hashCode toString 函數(shù)
數(shù)據(jù)類默認(rèn)的 copy equals hashCode toString 函數(shù)只管主構(gòu)造函數(shù)中的屬性,不管次構(gòu)造函數(shù)中的屬性或详,使用以上函數(shù)時(shí)需要注意
data class DataClass(var name: String , var age: Int) {
var coreInfo = ""
constructor(name: String): this(name, 20) {
coreInfo = "核心信息"
}
// 需要重寫才有次構(gòu)造函數(shù)中初始化的屬性值
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)
}
解構(gòu)聲明
解構(gòu)必須是按照順序聲明的系羞,且從 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")
}
運(yùn)算符重載
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
枚舉的值等價(jià)于枚舉本身
enum class Week {
星期一,
星期二,
星期三,
星期四,
星期五,
星期六,
星期日;
}
fun main() {
println(Week.星期一)
// 枚舉的值等價(jià)于枚舉本身
println(Week.星期一 is Week) // true
}
枚舉類定義函數(shù)
data class LimbsInfo(var name: String, var length: Int) {
fun show() {
println("${name}的長(zhǎng)度是$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},長(zhǎng)度:${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))
}
代數(shù)數(shù)據(jù)類型
when 判斷枚舉值
enum class Exams {
Fraction1,
Fraction2,
Fraction3,
Fraction4
}
class Teacher(private val exam: Exams) {
fun show() =
when (exam) {
Exams.Fraction1 -> "不及格"
Exams.Fraction2 -> "及格"
Exams.Fraction3 -> "優(yōu)良"
Exams.Fraction4 -> "優(yōu)秀"
// else -> 上面已經(jīng)包含全部值霸琴,可省略
}
}
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 -> "優(yōu)良"
is Exams.Fraction4 -> "優(yōu)秀,學(xué)生姓名是: ${(this.exam as Exams.Fraction4).name}"
}
}
fun main() {
println(Teacher(Exams.Fraction3).show())
println(Teacher(Exams.Fraction4("zhangsan")).show())
}
數(shù)據(jù)類使用條件
1. 服務(wù)器返回的響應(yīng)數(shù)據(jù) Java Bean 基本可以使用數(shù)據(jù)類
2. 數(shù)據(jù)類必須有至少一個(gè)參數(shù)的主構(gòu)造函數(shù)
3. 數(shù)據(jù)類必須有參數(shù)
4. 數(shù)據(jù)類不能使用 open abstract inner scaled 等修飾
5. 需要 比較梧乘、copy澎迎、toString、解構(gòu) 等豐富功能時(shí)选调,可使用數(shù)據(jù)類
六火诸、
接口的定義
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 = "鼠標(biāo)插入U(xiǎn)SB接口") : 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 = "鍵盤插入U(xiǎn)SB接口"
get() {
println("你get了$field")
return field
}
set(value) {
field = value
println("你set了$value")
}
override fun insert(): String = "Keyboard $versionInfo: $deviceInsertInfo"
}
接口的默認(rèn)實(shí)現(xiàn)
雖然接口成員可以通過(guò) get set 給其賦默認(rèn)值十偶,但是不建議這樣做,因?yàn)榻涌诒緛?lái)就是用來(lái)定義標(biāo)準(zhǔn)的
interface IUSB {
// 接口成員不論是val還是var茸苇,都不能給其賦值(但有其它方法:get)
// 任何類各吨、接口的val成員是只讀的枝笨,不能動(dòng)態(tài)改變其值(但是有其它方法:set)
val versionInfo: String
get() = (1..100).shuffled().first().toString()
// set(value) val 不能有set
val deviceInsertInfo: String
get() = "接入設(shè)備"
// 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("萬(wàn)能輸出器:$obj")
}
data class Student2(val name: String, val age: Int, val sex: Char)
fun main() {
val student = Student2("zhangsan", 22, 'm')
KtBaseTClass(student).show()
}
泛型函數(shù)
與 let apply also run 等結(jié)合使用
class KtClassGetter<T>(private val isOk: Boolean, private val obj: T) {
fun getClass() {
obj.takeIf { isOk }
}
}
泛型類型轉(zhuǎn)換
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關(guān)鍵詞(動(dòng)態(tài)參數(shù))
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() {
// 由于傳遞的動(dòng)態(tài)參數(shù)中袁铐,包含多種類型,所以泛型真正的類型是 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) {
"轉(zhuǎn)化為String: $it"
}
println(r)
val p2 = KtVarargClass("zhangsan", "lisi", "wangwu", isMap = true)
println(p2.showObj(0))
// p2傳遞的動(dòng)態(tài)參數(shù)中,只包含String類型徙融,所以it的類型為String?
val r2 = p2.mapObj(1) {
it?.length
}
println(r2)
}
[ ]操作符
class KtGetOperator<T>(vararg val objects: T, var isR: Boolean = true) {
// []運(yùn)算符重載
operator fun get(index: Int): T? {
return objects[index].takeIf { isR }
}
}
fun main() {
val p = KtGetOperator("zhangsan", "lisi", null)
// 只要有一個(gè)元素為null洒缀,那么所有的元素都是 String?
val r: String? = p.get(1)
println(p.get(0))
println(p.get(2))
}
out 協(xié)變
在父類泛型聲明處,可以接受子類
// 生產(chǎn)者 out T 協(xié)變欺冀,T只能被讀取树绩,不能修改
interface Producer<out T> {
// 編譯錯(cuò)誤:T不能被修改
// fun consumer(item: T)
fun product(): T
}
// 消費(fèi)者 in T 逆變,T不能被讀取隐轩,只能修改
interface Consumer<in T> {
// 編譯錯(cuò)誤:T不能被修改
fun consumer(item: T)
// 編譯錯(cuò)誤:T不能被讀取
// fun product(): T
}
// 生產(chǎn)者與消費(fèi)者 T 默認(rèn)情況下是不變饺饭,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("生產(chǎn)者 Animal")
return Animal()
}
}
class ProducerClass2: Producer<Human> {
override fun product(): Human {
println("生產(chǎn)者 Human")
return Human()
}
}
class ProducerClass3: Producer<Man> {
override fun product(): Man {
println("生產(chǎn)者 Man")
return Man()
}
}
class ProducerClass4: Producer<Woman> {
override fun product(): Woman {
println("生產(chǎn)者 Woman")
return Woman()
}
}
fun main() {
val p1: Producer<Animal> = ProducerClass1()
// ProducerClass2聲明的泛型為Human职车,p2聲明為Animal不報(bào)錯(cuò)瘫俊,是因?yàn)榉盒吐暶鳛?out,如果非 out 會(huì)報(bào)錯(cuò)
// out 相當(dāng)于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
使用場(chǎng)景:
in 逆變:只能修改扛芽,不能讀取
out 協(xié)變:只能讀取,不能修改
class SetClass<in T> {
fun set1(item: T) {
}
}
class GetClass<out T>(_item: T) {
val item = _item
fun get1(): T {
return item
}
}
refield 關(guān)鍵詞
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("產(chǎn)生的隨機(jī)對(duì)象:$randomObj")
// 如果不聲明 reified T积瞒,it as T 會(huì)報(bào)錯(cuò)
// 注意 as T?川尖,當(dāng)takeIf條件不成立時(shí),null as T 會(huì)導(dǎo)致崩潰赡鲜,所以要 T?
return randomObj.takeIf { it is T } as T? ?: defaultAction()
}
}
fun main() {
val result = RandomClass().randomOrDefault<ObjectClass1> {
println("使用默認(rèn)值")
ObjectClass1("zhangsan", 22, 'm')
}
println("結(jié)果:$result")
}
定義擴(kuò)展函數(shù)
類名.擴(kuò)展函數(shù)名()
class KtExtensionBaseClass(val name: String, val age: Int, val sex: Char)
// 擴(kuò)展函數(shù)空厌,會(huì)自動(dòng)生成this引用
fun KtExtensionBaseClass.show() = println("顯示信息:name:$name, age:$age, sex:$sex")
// 其它框架中的類也可以添加擴(kuò)展函數(shù)
fun String.addAction(count: Int) = this + "@".repeat(count)
fun main() {
KtExtensionBaseClass("zhangsan", 22, 'm').show()
println("lisi".addAction(3))
}
在超類上定義擴(kuò)展函數(shù)
1. 自己定義的擴(kuò)展函數(shù)不能重復(fù)
2. Kotlin內(nèi)置的擴(kuò)展函數(shù),我們可以重新定義進(jìn)行覆蓋
fun Any.showContent() = println("內(nèi)容是:$this")
fun Any.showContent2(): Any {
println("內(nèi)容2是:$this")
return this
}
fun File.readLine() {
println("覆蓋系統(tǒng)的擴(kuò)展函數(shù)")
}
data class ResponseResult(val msg: String, val code: Int)
fun main() {
ResponseResult("success", 200).showContent()
"zhangsan".showContent2().showContent()
File("").readLines()
}
泛型擴(kuò)展函數(shù)
fun <T> T.showContentInfo() = println("${if (this is String) "字符串長(zhǎng)度是:$length" else "不是字符串银酬,內(nèi)容是:$this"}")
fun commonFun() {}
fun main() {
"test".showContentInfo()
val p = 123
p.showContentInfo()
commonFun().showContentInfo()
}
標(biāo)準(zhǔn)函數(shù)與泛型擴(kuò)展函數(shù)
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"
擴(kuò)展屬性
val String.myInfo
get() = "zhangsan"
fun main() {
println("".myInfo)
}
可空類型的擴(kuò)展函數(shù)
類名?.擴(kuò)展函數(shù)名()
fun String?.outputValue(defaultValue: String) = println(this ?: defaultValue)
fun main() {
val nullValue: String? = null
nullValue.outputValue("默認(rèn)值1")
val value = "zhangsan"
value.outputValue("默認(rèn)值2")
}
infix 關(guān)鍵詞
infix:中綴表達(dá)式嘲更,可以簡(jiǎn)化代碼
1. 需要與擴(kuò)展函數(shù)一起使用
2. 需要在函數(shù)的參數(shù)中傳遞一個(gè)參數(shù)
private infix fun <C1, C2> C1.gogo(c2: C2) {
println("中綴表達(dá)式:第一個(gè)參數(shù):$this")
println("中綴表達(dá)式:第二個(gè)參數(shù):$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
}
定義擴(kuò)展文件
在實(shí)際中比較有用揩瞪,可以把很多擴(kuò)展操作定義在一個(gè)文件中赋朦,使用的時(shí)候引入,方便管理
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)
}
重命名擴(kuò)展
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 函數(shù)詳解
// INPUT.myApply李破,泛型擴(kuò)展函數(shù): 讓所有的類型宠哄,都可以 xxx.myApply 調(diào)用
// INPUT.():讓匿名函數(shù)持有 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),領(lǐng)域?qū)S谜Z(yǔ)言
// apply5函數(shù)嗤攻,就是DSL編程范式毛嫉,定義輸入輸出等規(guī)則。
// 1.定義lambda規(guī)則標(biāo)準(zhǔn)妇菱,輸入 必須是Context類承粤,才能調(diào)用apply5函數(shù)
// 2.定義lambda規(guī)則標(biāo)準(zhǔn)暴区,輸出 始終返回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 {
// 同時(shí)持有this和it
toast("success")
// it == String
toast(it)
toast(name)
}
println(context)
File("xxx")
.applyFile { fileName, content ->
println("文件名:$fileName, 內(nèi)容:$content")
}
.applyFile { fileName, content ->
}
}
變換函數(shù)-map
map:生成一個(gè)新的集合,新集合中每個(gè)元素根據(jù)lambda表達(dá)式進(jìn)行轉(zhuǎn)換
fun main() {
val list = listOf("zhansan", "lisi", "wangwu")
list
.map {
"姓名:$it"
}
.map {
println("$it")
}
}
變換函數(shù)-flatMap
flatMap:生成一個(gè)新的集合辛臊,新集合中每個(gè)元素都是集合仙粱,新集合中每個(gè)元素根據(jù)lambda表達(dá)式進(jìn)行轉(zhuǎn)換生成
fun main() {
val list = listOf("zhansan", "lisi", "wangwu")
// flatMap返回的是 List<List<String>>,但最終會(huì)進(jìn)行平鋪返回 List<String>
val newList: List<String> = list.flatMap {
listOf("$it 在學(xué)習(xí)Java", "$it 在學(xué)習(xí)Kotlin")
}
println(newList)
}
// [zhansan 在學(xué)習(xí)Java, zhansan 在學(xué)習(xí)Kotlin, lisi 在學(xué)習(xí)Java, lisi 在學(xué)習(xí)Kotlin, wangwu 在學(xué)習(xí)Java, wangwu 在學(xué)習(xí)Kotlin]
過(guò)濾函數(shù)-filter
filter:生成一個(gè)新的集合彻舰,新集合中每個(gè)元素符合給定的規(guī)則
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
}
}
合并函數(shù)-zip
zip:根據(jù)兩個(gè)集合中的元素生成一個(gè)新的集合伐割,與原集合index一致,并且新集合元素?cái)?shù)量為原兩個(gè)集合數(shù)量最小值
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
}
函數(shù)式編程
簡(jiǎn)化代碼刃唤,例如 zip 函數(shù)的 Java 實(shí)現(xiàn)遠(yuǎn)比 Kotlin 實(shí)現(xiàn)所用代碼多很多
Kotlin與Java互操作性和可空性
Kotlin調(diào)用Java代碼時(shí)隔心,Java給Kotlin的值,都是類似于 String!透揣,
所以kotlin在接受Java給的值時(shí)济炎,直接把類型聲明為可空?,避免代碼出錯(cuò)
fun main() {
// 錯(cuò)誤
println(JavaClass().info1.length)
println(JavaClass().info2.length) // 崩潰
// 錯(cuò)誤
val info1 = JavaClass().info1
val info2 = JavaClass().info2
println(info1.length)
println(info2.length) // 崩潰
// 正確
// Kotlin調(diào)用Java代碼時(shí)辐真,Java給Kotlin的值须尚,都是類似于 String!
val info11 = JavaClass().info1
val info21 = JavaClass().info2
println(info11?.length)
println(info21?.length)
// 正確,推薦
// Kotlin調(diào)用Java代碼時(shí)侍咱,Java給Kotlin的值耐床,都是類似于 String!
// 所以kotlin在接受Java給的值時(shí),直接把類型聲明為可空?楔脯,避免代碼出錯(cuò)
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. 懶漢式+雙重校驗(yàn)安全: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文件自動(dòng)生成Java類時(shí),指定類名稱
@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昧廷,會(huì)剔除自動(dòng)生成的getXxx()方法,在Java調(diào)用該屬性時(shí)可直接調(diào)用
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皆串,只能調(diào)用getXxx()方法獲取屬性
for (String name : persons.getNames()) {
System.out.println(name);
}
// 加了 @JvmField,可以直接調(diào)用屬性眉枕,不會(huì)再生成getXxx()方法
for (String name : persons.names) {
System.out.println(name);
}
}
}
注解 @JvmOverloads 與 Kotlin
@JvmOverloads 用于Kotlin函數(shù)谤牡,在Java調(diào)用Kotlin函數(shù)時(shí)姥宝,可以使用其定義好的參數(shù)默認(rèn)值
原理是編譯器會(huì)生成多個(gè)傳遞不同參數(shù)的重載方法腊满,給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函數(shù)的參數(shù)默認(rèn)值
KtBaseKt.toast("zhangsan"); // Kotlin函數(shù)加上@JvmOverloads后断序,Java可以享用參數(shù)默認(rèn)值
}
}
注解 @JvmStatic 與 Kotlin
MyObject.kt
class MyObject {
companion object {
@JvmField // 編譯時(shí)會(huì)把TARGET移出Companion內(nèi)部類
val TARGET = "公園"
@JvmStatic // 編譯時(shí)會(huì)把showAction方法移出Companion內(nèi)部類
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流纹,只能通過(guò).Companion調(diào)用
MyObject.Companion.showAction("zhangsan"); // 不加@JvmStatic,只能通過(guò).Companion調(diào)用
System.out.println(MyObject.TARGET); // 加上@JvmField违诗,可以直接調(diào)用,并且會(huì)剔除getXxx()方法
MyObject.showAction("zhangsan"); // 加上@JvmStatic疮蹦,可以直接調(diào)用
}
}
手寫事件變換操作符-create
fun main() {
create {
"zhangsan"
}
}
class RxCoreClass() {
}
inline fun <OUTPUT> create(action: () -> OUTPUT): RxCoreClass {
}
手寫事件變換操作符-中轉(zhuǎn)站
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 {
"長(zhǎng)度是:$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())