項目未來可能需要使用
kotlin
開發(fā),所以特此記錄一下學(xué)習(xí)筆記冕象,僅供參考墓律,方便后期查詢。已同步到GitHub
上:KotlinTest
Kotlin 簡介
kotlin
的目標(biāo)是成為一門全棧語言低斋,主要有以下的特點:
- 已經(jīng)成為
Android
的官方推薦語言 - 百分百的和
java
兼容,兩者可以相互轉(zhuǎn)換 -
JS
讽挟、JVM
、Native
多平臺開發(fā)
數(shù)據(jù)類型
1. 基本類型
Boolean true/false
Double 64
Float 32
Long 64
Int 32
Short 32
Byte 8
val aChar = '0'
val bChar = '我'
val cChar = '\u000f'
Char
類型的轉(zhuǎn)義字符
\t 制表符
\b 光標(biāo)后退一個字符
\n 回車
\r 光標(biāo)回到行首
\' 單引號
\" 雙引號
\\ 反斜杠
\$ 美元符號丸冕,Kotlin 支持美元符號開頭的字符串模板
2. 基本類型的轉(zhuǎn)換
不可隱式轉(zhuǎn)換
val anInt: Int = 5
val aLong: Long = anInt.toLong()
必須得通過.to類型
的方式進(jìn)行數(shù)據(jù)的轉(zhuǎn)換
字符串
一串
Char
-
用雙引號""引起來
val aString: String = "Hello World!"
-
字符串比較
a == b 表示比較內(nèi)容 類似 Java 中的 equals
a === b 表示比較對象是否相同 -
字符串模板
println("hello, $name") -> "hello, 小明"
3. Koltin 中的類和對象初始化
類的定義
- 類耽梅,一個抽象的概念
- 具有某些特征的事物的概括
- 不特定指代任何一個具體的事物
一般寫法:
/**
* 其中類參數(shù)如果加上 var 修飾,那么他便是成員變量胖烛,反之則是普通的參數(shù)
*/
class Student(var name: String, var age: Int){
init {
// ... 相當(dāng)于構(gòu)造函數(shù)中的代碼
}
}
對象
是一個具體的概念眼姐,與類相對
描述某一個類的具體個體
-
舉例:
某些人、領(lǐng)導(dǎo)的車等等
類和對象的關(guān)系
- 一個類通撑宸可以有很多歌具體的對象
- 一個對象本質(zhì)上只能從屬一個類
- 某一個人众旗,他是工程師,但本質(zhì)上還是屬于人這一類
一般寫法:
val student: Student = Student("xiaweizi", 23)
類的繼承
- 提取多個類的共性得到一個更為抽象的類趟畏,即父類
- 子類擁有父類的一切特征
- 子類也可以定義自己的特征
- 所有的類最終繼承自
Any
,類似于java
中的Object
4. 空類型和智能轉(zhuǎn)換
空類型
// 定義
val notNull: String = null // 錯誤贡歧,不可能為空
val nullanle: String? = null // 正確,可以為空
// 使用
notNull.length // 正確拱镐,不可能為空所以可以直接使用
nullable.length // 有可能為空艘款,不能直接獲取長度
// 要想獲取長度持际,可以通過以下兩者方式
nullable!!.length // 正確沃琅,強制認(rèn)定 nullable 不可能為空,如果為空則會拋出空指針異常
nullable?.length // 正確蜘欲,若 nullable 為空益眉,則返回 null
智能類型轉(zhuǎn)換
val child: Child = parent as Child // 類似于 Java 的類型轉(zhuǎn)換,失敗則拋出異常
val child: Child = parent as? Child // 如果轉(zhuǎn)換失敗姥份,返回 null
編譯器智能識別轉(zhuǎn)換:
val parent: Parent = Child()
if (parent is Child) {
// parent 直接調(diào)用子類方法,不需要再進(jìn)行強制轉(zhuǎn)換
}
val string: String = null
if (string != null) {
// string.length 可以直接調(diào)用length 方法
}
5. 區(qū)間
一個數(shù)學(xué)上的概念郭脂,表示范圍, ClosedRange
的子類澈歉,IntRange
最常用
基本用法:
0..100 --> [0, 100]
0 until 100 --> [0, 100)
i in 0..100 表示 i 是否在區(qū)間[0, 100]中
6. 數(shù)組
基本寫法:
val ints: IntArray = IntArrayOf(1,2,3,5)
var charArray: CharArray = charArrayOf('a', 'b', 'c', 'd', 'e')
var stringArray: Array<String> = arrayOf("aa", "bb", "cc", "dd", "e")
基本操作:
print(charArray[index])
ints[0] = 2
ints.length
cahrArray.joinToString("") // 講 char 數(shù)組轉(zhuǎn)換成字符串
stringArray.slice(1..4) // 取出區(qū)間里的值
程序結(jié)構(gòu)
1. 常亮和變量
常量
val a = 2
類似 Java 中的 final
不可被重復(fù)賦值
運行時常量:val x = getX()
編譯期常量:const val x = 2
變量
var a = 2
a = 3 // 可以被再次賦值
類型推導(dǎo)
val string = "Hello" // 推導(dǎo)出 String 類型
val int = 5 // 推導(dǎo)出 Int 類型
var x = getString() + 5 // String 類型
2. 函數(shù) Function
以特定功能組織起來的代碼塊
// 最簡單的打印信息展鸡,無返回的方法
fun printMessage(message: String):Unit{
println("$message")
}
// 擁有返回值得方法
fun sum(first: Int, second: Int):Int {
return first + second
}
// 可以簡化成:
fun sum(first: Int, second: Int) = first + second
// 或者更簡單的匿名函數(shù)
val result = fun(first: Int, second: Int) = first + second
3. Lambda 表達(dá)式
其實又是匿名函數(shù)
一般形式:
{傳入?yún)?shù) -> 函數(shù)體,最后一行是返回值}
// 例如
val sum = {first: Int, second: Int -> first + second}
val printMessage = {message: String -> println(message)}
類型標(biāo)識
() -> Unit // 無參埃难,返回值為 null
(Int) -> Int // 傳入整型莹弊,返回一個整型
(String, (String) -> String) -> Boolean // 傳入字符串、Lambda 表達(dá)式涡尘,返回Boolean
Lambda 表達(dá)式的簡化
- 函數(shù)參數(shù)調(diào)用時最后一個
Lambda
可以移出去 - 函數(shù)參數(shù)只有一個
Lambda
忍弛,調(diào)用時小括號可以省略 -
Lambda
只有一個參數(shù)可默認(rèn)為it
- 入?yún)ⅰ⒎祷刂蹬c形參一致的函數(shù)可以用函數(shù)引用方式作為實參傳入
4. 成員變量和成員方法
成員變量的聲明
// 第一種是在構(gòu)造函數(shù)中聲明
class Student(var age: Int, name: String){
// age 是成員變量 name 是局部變量
}
// 第二種是在函數(shù)體內(nèi)聲明
var a = 0
get() {
field += 1
return field
}
set(value) {
println("set)
field = value + 1
}
// 可以進(jìn)行對 get 和 set 方法的重新定義
// 屬性的初始化盡量在構(gòu)造方法中完成
// var 用 lateinit 延遲初始化考抄, val 用 lazy
lateinit var sex: String
val person: Person by lazy {
Person()
}
成員方法
在類中直接聲明方法可以直接調(diào)用,包括lambda
表達(dá)式
// 方法的聲明
fun sum(a: Int, b: Int) = a + b
val sum1 = {a: Int, b: Int -> a + b}
// 方法的調(diào)用
println(person.sum(1,2))
println(person.sum1(3,5))
5. 運算符
在java
中運算符是不能重新定義重載的细疚,只能按照原先的邏輯進(jìn)行計算
而Kotlin
則可以重新定義運算符,使用operator
關(guān)鍵字川梅,舉了例子:
// 定義一個用于計算復(fù)數(shù)的類
class Complex(var real: Double, var imaginary: Double) {
operator fun plus(other: Complex): Complex{
return Complex(real+other.real, imaginary+other.imaginary)
}
// 重新 toString 方法
overrride fun toString(): String {
return "$real + ${imaginary}i"
}
}
// 使用
val complex1 = Complex(1, 2)
val complex2 = Complex(2, 3)
println(complex1 + complex2)
// 輸出結(jié)果為
"3 + 5i"
關(guān)鍵就是這個方法疯兼,方法名必須是plus
或者其他官方定義的運算符然遏,參數(shù)有且僅有一個,類型自定義吧彪,返回值意識可以自定義的.
operator fun plus(other: Complex): Complex{
return Complex(real+other.real, imaginary+other.imaginary)
}
6. 表達(dá)式
中綴表達(dá)式
通過infix
關(guān)鍵字修復(fù)方法啦鸣,那么就可以不用通過 對象.方法() 的方式調(diào)用,而是直接 對象 方法名 參數(shù)的方式調(diào)用来氧。舉了例子
class Student(var age: Int){
infix fun big(student: Student): Boolean {
return age > student.age
}
}
// 如果沒有 infix 的調(diào)用方式:
println(Student(23).big(Student)(12))
// 如果使用 infix 修飾的調(diào)用方式:
println(Student(23) big Student(12))
if
表達(dá)式
直接來個例子
val a = 20
val b = 30
val flag: Int = if(a > b) a else b
When
表達(dá)式
加強版的 switch
诫给,支持任意類型, 支持純粹表達(dá)式條件分支(類似if
)啦扬,舉個栗子:
val a = 5
when(a) {
is Int -> println("$a is Int")
in 1..6 -> println("$a is in 1..6")
!in 1..4 -> println("$a is not in 1..4")
else -> {
println("null")
}
}
for
循環(huán)
基本寫法
for (element in elements)
while
循環(huán)
基本寫法
while() {
}
do {
} while()
跳過和終止循環(huán)
跳過當(dāng)前循環(huán)用 continue
終止循環(huán)用 break
6. 異常捕獲
同樣也是表達(dá)式中狂,可以用來賦值,舉個例子
return try{
x/y
}
catch(e: Exception) {
0
} finally {
//...
}
如果沒有異常則返回x/y
,否則返回0
,finally
中的代碼無論如何還是要執(zhí)行的扑毡。
7. 具名參數(shù)胃榕、變長參數(shù)和默認(rèn)參數(shù)
具名參數(shù):給函數(shù)的實參附上形參
fun sum(first: Int, second: Int) = first + second
sum(second = 2, first = 1)
變長參數(shù):用varary
修飾,使用起來是和數(shù)組一樣瞄摊,某個參數(shù)可以接收多個值勋又,可以不作為最后一個參數(shù),如果傳參時有歧義换帜,需要使用具名參數(shù)楔壤。
fun hello(vararg ints: Int, string: String) = ints.forEach(println(it))
hello(1,3,4,5,string = "hello")
// 如果最后一個參數(shù)也是 Int
fun hello(varary ints: Int, anInt: Int)
// 創(chuàng)建數(shù)組
val arrayInt: IntArray = intArrayOf(1, 2, 3, 4)
hello(ints = *arrayInt, anInt = 2)
默認(rèn)參數(shù):就是給參數(shù)傳入一個默認(rèn)的值
fun hello(anInt: Int = 1, string: String)
hello(string = "aaa")
面向?qū)ο?/h2>
1. 繼承
繼承語法要點:
- 父類需要
open
才可以被繼承 - 父類方法、屬性需要
open
才可以被覆寫 - 接口惯驼、接口方法蹲嚣、抽象類默認(rèn)為
open
- 覆寫父類(接口)成員需要
override
關(guān)鍵字
語法要點:
class A: B(), C, D
- 繼承類時實際上調(diào)用了父類的構(gòu)造方法
- 類只能單繼承,接口可以多實現(xiàn)
接口代理:
一個類可以直接將自己的任務(wù)委托給接口的方法實現(xiàn)祟牲,舉個例子:
interface Drive{
fun drive()
}
interface Sing{
fun sing()
}
class CarDrive: Drive{
override fun drive() {
println("我會開車呦")
}
}
class LoveSing: Sing{
override fun sing() {
println("我會唱歌呦")
}
}
class Manager(drive: Drive, sing: Sing): Drive by drive, Sing by sing
fun main(args: Array<String>) {
val carDrive = CarDrive()
val loveSing = LoveSing()
val manager = Manager(carDrive, loveSing)
manager.drive()
manager.sing()
}
這樣隙畜,manager
不用做任何事情,完全交付給接口實現(xiàn).
接口方法沖突:
接口方法可以有默認(rèn)實現(xiàn)说贝,通過super<父類名>
.方法名
interface A{
fun a() = 0
}
interface B{
fun a() = 1
}
interface C{
fun a() = 2
}
class D(var aInt: Int): A,B,C{
override fun a(): Int {
return when(aInt){
in 1..10 ->{
super<A>.a()
}
in 11..100 ->{
super<B>.a()
}
else -> {
println("dd")
super<C>.a()
}
}
}
}
2. 類及成員的可見性
跟java
類似议惰,private、protected乡恕、public
言询,其中internal
代表的是模塊內(nèi)可見
3. Object
相當(dāng)于Java
中的單例模式,有以下特點
只有一個實例的類
不能自定義構(gòu)造方法
可以實現(xiàn)接口几颜、繼承父類
-
本質(zhì)上就是單例模式最基本的實現(xiàn)
interface getDataSuccess{ fun success() } abstract class getDataField{ abstract fun failed() } object NetUtil: getDataField(), getDataSuccess{ override fun success() { println("success") } override fun failed() { println("failed") } val state: Int = 0 fun getData(): String = "請求成功" }
3. 伴生對象和靜態(tài)成員
相當(dāng)于java
中的靜態(tài)方法
每個類可以對應(yīng)一個伴生對象
伴生對象的成員全局獨一份
-
如果
java
中想直接調(diào)用kotlin
中的靜態(tài)方法或者靜態(tài)變量倍试,可以考慮使用JvmField JvmStatic
.open class Util private constructor(var anInt: Int) { companion object { @JvmStatic fun plus(first: Int, second: Int) = first + second fun copy(util: Util) = Util(util.anInt) @JvmField val tag = "tag" } }
4. 方法的重載
通過給方法的參數(shù)配置默認(rèn)值,即可實現(xiàn)方法的重載蛋哭,按理說县习,一切可以擁有默認(rèn)值的方法重載才是合理的方法重載。
名稱形同、參數(shù)不同躁愿,跟返回值沒有關(guān)系
class OverLoadTest {
@JvmOverLoads
fun a(anInt: Int = 0, string: String="") = 1
}
val test = OverLoadTest()
test.a(1, "")
test.a()
test.a(anInt = 2)
test.a(string = "")
使用JvmOverLoads
是為了方便Java
中調(diào)用方法的重載.
5. 擴展方法
kotlin
中的擴展方法叛本,我認(rèn)為相當(dāng)于java
中的代理模式,拿到被代理的對象彤钟,然后進(jìn)行一系列的操作来候。
fun String.add(anInt: Int): String {
var sb = StringBuilder()
for (i in 0 until anInt) {
sb.append(this)
}
return sb.toString()
}
operator fun String.times(anInt: Int): String {
var sb = StringBuilder()
for (i in 0 until anInt) {
sb.append(this)
}
return sb.toString()
}
// 使用
var string = "xiaweizi"
println(string.add(5))
println(string * (3))
6. 屬性代理
類似之前說的var anInt: Int by lazy{2}
,懶賦值就是使用的屬性代理,來看個例子:
fun main(args: Array<String>) {
val a: Int by DelegatesTest()
println(a)
var b: Int by DelegatesTest()
b = 3
println(b)
}
class DelegatesTest {
private var anInt: Int? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
println("getValue")
return anInt?:0
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int): Unit {
println("setValue")
this.anInt = value
}
}
val
對應(yīng)getValue
逸雹,var
對應(yīng)getValue和setValue
方法营搅,這個時候聲明的屬性就全權(quán)交付給DelegatesTest
類中的anInt
代理,當(dāng)anInt
為空的時候返回0
梆砸,否則返回anInt
.
7. JavaBean
使用data
修飾類转质,類似java
中的javaBean
,默認(rèn)實現(xiàn)了set get toString
等方法,并擁有componentN
方法.
不過有個缺點就是帖世,無法被繼承休蟹,沒有無參構(gòu)造函數(shù),可以通過安裝allOpen
和noArg
插件解決這個問題.
data class UserBean(var name: String, var age: Int)
val userBean: UserBean = UserBean("小芳", 23)
println(userBean.name)
println(userBean.toString())
println(userBean.component1())
println(userBean.component2())
val (name, age) = userBean
println("name: $name")
println("age: $age")
至于這種寫法
val (name, age) = userBean
日矫,是因為定義了component1
的運算符
class Complex{
operator fun component1() = "你好呀"
operator fun component2() = 2
operator fun component3() = 'a'
}
val complex = Complex()
val (a, b, c) = complex
println(a + b + c)
使用起來也是很簡單的
8. 內(nèi)部類
- 定義在類內(nèi)部的類
- 與類成員有相似的訪問控制
- 默認(rèn)是靜態(tài)內(nèi)部類赂弓,非靜態(tài)用
inner
關(guān)鍵字 -
this@Outter
this@Inner
的用法 - 匿名內(nèi)部類
- 沒有定義名字的內(nèi)部類
- 類名編譯時生成,類似
Outter$1.class
- 可繼承父類哪轿,實現(xiàn)多個接口盈魁,與
Java
注意區(qū)別
舉個例子:
class Outer{
var string: String = "outer"
class Inner1{
var string: String = "inner1"
fun sum(first: Int, second: Int) = first + second
}
inner class Inner2{
var string: String = "inner2"
fun cha(first: Int, second: Int) = first - second
fun getInnerField() = this.string
fun getOuterField() = this@Outer.string
}
}
fun main(args: Array<String>) {
val inner1 = Outer.Inner1()
val inner2 = Outer().Inner2()
println(inner1.sum(1, 2))
println(inner2.cha(2, 1))
println(inner2.getInnerField())
println(inner2.getOuterField())
}
匿名內(nèi)部類:
val listener: onClickListener = object : Father(), Mother, onClickListener{
override fun sing() {
println("mother sing")
}
override fun teach() {
println("father teach")
}
override fun onClick() {
println("匿名內(nèi)部類")
}
}
使用
Object
實現(xiàn)匿名內(nèi)部類
9. 枚舉和密封類
枚舉是對象可數(shù),每個狀態(tài)相當(dāng)于每個對象缔逛,是可以傳構(gòu)造參數(shù)的
密封類時子類可數(shù)备埃,在kotlin
大于1.1子類只需要與密封類在同一個文件加姓惑,保護子類的位置
sealed class SealedClassTest{
class sum(first: Int, seocnd: Int): SealedClassTest()
class cha(first: Int, seocnd: Int): SealedClassTest()
object Bean: SealedClassTest()
}
enum class HttpStatus(val anInt: Int){
SUCCESS(0), FAILED(1), LOADING(2)
}
fun main(args: Array<String>) {
val class1 = SealedClassTest.cha(1, 2)
println(HttpStatus.SUCCESS)
}
高階函數(shù)
1. 基本概念
- 傳入或者返回函數(shù)的函數(shù)
- 函數(shù)引用
::println
- 帶有
Receiver
的引用pdfPrinter::println
有三種顯示
// 1. 包級函數(shù)
intArray.forEach(::print)
// 2. 類.方法
intArray.forEach(Int::addOne)
fun Int.addOne(): Unit {
println("addOne:$this")
}
// 3. 對象.方法
intArray.forEach(AddTwo()::addTwo)
class AddTwo {
fun addTwo(anInt: Int): Unit {
println("addTwo:$anInt")
}
}
2. 常用的高階函數(shù)
常用的高階函數(shù)還是有很多的褐奴,會簡單的使用例子即可:
// 遍歷
fun forEachTest() {
val strings: Array<String> = arrayOf("aa", "ee", "bb", "ll")
strings.forEach { println(it) } // 遍歷每一個值
strings.forEachIndexed { index, s -> println("index:$index,String:$s") } // 遍歷 下標(biāo)和值一一對應(yīng)
}
// 重新拷貝一個值
fun mapTest() {
val strings: Array<String> = arrayOf("aa", "ee", "bb", "ll")
var map = strings.map { "$it-test" }
map.forEach { print("$it\t") }
}
// 將集合合體
fun flatMapTest() {
val lists = listOf(1..10,
2..11,
3..12)
var flatMap = lists.flatMap {
it.map {
"No.$it"
}
}
flatMap.forEach(::println)
}
fun reduceTest() {
val ints = listOf(2, 3, 4, 5)
println(ints.reduce { acc, i ->
acc + i
})
}
// 字符串連接
fun foldTest(){
val ints = listOf(2, 3, 4, 5)
println(ints.fold(StringBuffer(), { acc, i -> acc.append("$i,") }))
println(ints.joinToString(","))
}
fun filterTest() {
val ints = listOf(1, 2, 3, 4, 5, 6)
println(ints.filter { element -> element % 2 == 0 })
}
// 當(dāng)值不是奇數(shù)就去,遇到偶數(shù)就停止了
fun takeWhileTest() {
val ints = listOf(1, 3, 3, 4, 5, 6)
println(ints.takeWhile { it % 2 != 0 })
}
fun letTest() {
findPerson()?.let { (name, age) -> println("name:$name, age:$age") }
findPerson()?.apply { println("name:$name, age:$age") }
with(findPerson()!!) { println("name:$name, age:$age") }
}
data class Person(val name: String, val age: Int)
fun findPerson(): Person? {
return Person("aa", 23)
}
3. 復(fù)合函數(shù)
有點類似數(shù)據(jù)中的f(g(x))
fun main(args: Array<String>) {
val add1 = {int: Int ->
println("add1")
int + 1}
val add2 = {int : Int ->
println("add2")
int + 2}
var add3 = add1 addThen (add2)
println(add3(4))
}
infix fun <P1, P2, R> Function1<P1, P2>.addThen(function: Function1<P2, R>): Function1<P1, R> {
return fun(p: P1): R{
return function.invoke(this.invoke(p))
}
}
4. Currying
簡單來說就是多元函數(shù)變換成一元函數(shù)調(diào)用鏈?zhǔn)接诒校e個簡單的例子敦冬,這是優(yōu)化之前:
fun log(tag: String, out: OutputStream, message: String){
out.write("[$tag], $message".toByteArray())
}
優(yōu)化之后
fun log(tag: String)
= fun(out: OutputStream)
= fun(message: String)
= out.write("[$tag], $message".toByteArray())
5. 計算文件字符串個數(shù)的小例子
首先將字符串轉(zhuǎn)換成字符串?dāng)?shù)組:
val map: HashMap<Char, Int> = HashMap()
var toCharArray = File("build.gradle").readText().toCharArray()
通過分組的方式,統(tǒng)計每個字符串的個數(shù)唯沮,并打硬焙怠:
toCharArray.groupBy { it }.map { it.key to it.value.size }.forEach { println(it) }
kotlin
和java
的混合開發(fā)
1. 基本的交互操作
屬性讀寫
-
Kotlin
自動識別Java Getter/Setter
-
Java
操作Kotlin
屬性通過Getter/Setter
空安全類型
-
Kotlin
空安全類型的原理 - 平臺類型
Platform Type
-
Java
可以通過@Nullable、@NotNull
幾類函數(shù)的調(diào)用
- 包級函數(shù):靜態(tài)方法
- 擴展方法:帶
Receiver
的靜態(tài)方法 - 運算符重載:帶
Receiver
的對應(yīng)名稱的靜態(tài)方法
幾個常用的注解
-
@JvmField
:將屬性編譯為Java變量
-
@JvmStatic
:將對象的方法編譯成功Java
靜態(tài)方法 -
@JvmOverloads
:默認(rèn)參數(shù)生成重載方法 -
@JvmName
:制定Kotlin
文件編譯后的類名
NoArg 和 AllOpen
-
NoArg
為被標(biāo)注的類生成無參構(gòu)造 -
AllOpen
為被標(biāo)注的類去掉final
介蛉,允許被繼承
正則表達(dá)式
- 用
Raw
字符串定義正則表達(dá)式 -
Java
的Pattern
-
Kotlin
的Regex
舉個例子:
val source = "Hello This my phone number: 010-12345678."
val pattern = """.*(\d{3}-\d{8}).*"""
Regex(pattern).findAll(source).toList().flatMap(MatchResult::groupValues).forEach(::print)