本篇文章是對Kotlin語言入門學習(一)的補充,適用於有一定Java基礎的同學學習,在理解Kotlin的基本語法結構後,更重要的是加以實踐,方便更快更好的理解Kotlin語言。
一、Kotlin是什麼
Kotlin是一種基於JVM的靜態類型編程語言
Kotlin的入口是main()函數
Kotlin的特點:
1,極簡:語法簡潔優雅,類型系統中一切皆引用
2,空安全:?可空
3,多範式:Kotlin同時支持OOP與FP編程範式
4,可擴展:Kotlin可直接擴展函數與屬性
5,高階函數與閉包
6,支持快速實現DSL
7,智能推斷
Kotlin的工具平臺:雲端IDE,命令行REPL,IDEA
二、Kotlin語法基礎
throw表達式:在Kotlin中,throw是表達式,它的類型是特殊類型Nothing。該類型沒有值,與C、Java中的void意思一樣
Kotlin中有兩種類型的相等性:
引用相等=== !== (兩個引用指向同一對象,即值與引用都相等)
結構相等== != (使用equals()判斷,比較值)
Elvis操作符 ?: 在Kotlin中,Elvis操作符特定是跟null進行比較,如:y = x?:0 等價於:val y = if(x!==null) x else 0
非空斷言“!!”
Kotlin提供了斷言操作符“!!”,使得可空類型對象可以調用成員方法或者屬性(但遇見null,就會導致空指針異常)
Kotlin中沒有三元表達式,對應的是:if...else語句,如:if(true) 1 else 0。而Elvis操作符是精簡版的三元運算符
三、類型系統與可空類型
3.1類型系統
相對於Java語言,Kotlin語言去掉了原始數據類型,只有包裝類型
Kotlin系統類型分爲可空類型和不可空類型
Kotlin在類型T後面加個“?”,就表達了可空類型
fun main(args:Array<String>){
println(strLength(null))
println(strLength("abc"))
}
fun strLength(s:String?):Int{
return s?.length ?:0 //?.是安全調用符,?:是Elvis操作符
}
3.2特殊類型
Unit、Nothing、Any及其對應的可空類型Unit?、Nothing?、Any?
當一個函數沒有返回值的時候,用Unit來表示這個特徵,而不是null,但大多數時候,可省略,返回類型是Unit
Void對應Nothing?,其唯一可被訪問的返回值是null
如果一個函數的返回值是Nothing,意味着這個函數永遠不會有返回值
Any?是可空類型層次的根,Any是非空類型層次結構的根,Any?是Any的超集,Any?是Kotlin類型層次結構的最頂端
3.3類型檢測與類型轉換
Kotlin中的is和!is類似於Java中的instanceOf,類型匹配
as運算符用於類型轉換,as?爲可空轉換,如果類型不兼容,則返回null
在Kotlin中,父類是禁止轉換爲子類的
四、類與面向對象編程
Kotlin中沒有靜態屬性和方法,但可以使用關鍵字object聲明一個object單例對象,Kotlin中還提供了伴生對象,用companion object關鍵字聲明
數據類:只存儲數據,不包含操作行爲的類,Kotlin中使用關鍵字data class可創建一個只包含數據的類,類似於Java中的bean類
五、函數與函數式編程
5.1函數式編程
函數式編程(FP),使一種編程範式,即面向函數編程,核心思想在於解決問題的過程,把任務分解爲一個個的函數,最後通過函數組合來實現目標
比如,我們現在有一個字符串列表:val strList = listOf("a","ab","abc","abcd","abcde","abcdef","abcdefg")
現在我們想要過濾出字符串元素中長度是奇數的列表,那麼可以把這個問題的解決邏輯拆成兩個函數來組合實現:
val f = fun (x:Int) = x % 2 == 1 //判斷輸入的Int是否奇數
val g = fun (s:String) = s.length //返回輸入的字符串參數的長度
再使用函數h來封裝:
val h = fun (g:(String) -> Int,f:(Int -> Boolean):(String) -> Boolean){
return{ f(g(it)) }
}
這個h函數稍顯長,可通過類型別名typealias使代碼更簡潔
5.2Kotlin中的特殊函數
特殊函數主要有:run()、apply()、let()、also()、with()
run():使用時可使用run(其它函數),表示執行()內的函數,()可省略
apply():通過.方式調用,表示在run()的基礎上返回當前調用者對象
let():通過.方式調用,表示把當前調用對象作爲參數傳給let函數,如myfun().let{println(it)},則表示執行完myfun函數後,返回這傳給let()函數,最後打印結果爲myfun()返回值
also():通過.方式調用,表示在let()基礎上返回當前調用者
with():執行傳入的代碼塊,或者理解爲接收一個指定類型的對象去執行body函數
以上5種函數沒有給出具體示例,希望讀者找相關例子進行更好的理解並在實踐中加以應用
六、擴展函數
爲List擴展一個filter()函數
fun <T> List<T>.filter(predicate: (T) -> Boolean):MutableList<T>{
val result = ArrayList<T>()
this.forEach{
if(predicate(it)){
result.add(it)
}
}
return result
}
調用:
val list = mutableListOf(1,2,3,4,5,6,7)
val result = list.filter{
it%2==1
}
predicate(result) //[1,3,5,7]
擴展屬性:允許定義在類或者Kotlin文件中,不允許定義在函數中
七、集合類
集合類存放的都是對象的引用,而非對象本身,Kotlin中集合類分爲:可變集合類(Mutable)與不可變集合類(Immutable)
遍歷集合中的元素:forEach
list.forEach{
println(it)
}
映射函數map:
val list = listOf(1,2,3,4,5,6,7) //聲明並初始化一個List
list.map{it*it} //map函數對每個元素進行乘方操作,返回[1,4,9,16,25,36,49]
set和map類似
過濾函數filter:
data class Student(var id:Long,var name:String,var age:Int,var score:Int){ //聲明Student數據類
override fun toString():String{ //覆寫toString()函數
return "Student(id = $id,name = '$name',age = $age,score = $score)"
}
}
創建一個持有Student對象的List
val studentList = listOf(
Student(1,"Jack",18,90),
Student(2,"Rose",17,90),
Student(3,"Alice",16,70),
)
此時,如果我們想要過濾出大於等於18歲的學生,可以這樣寫:
studentList.filter{it.age>=18}
輸出結果:[Student(id = 1,name = "Jack",age = 18,score = 90)]
排序函數:
reversed()倒序
sorted()升序
元素去重:
distinct()
八、泛型
類型上界
fun <T:Comparable<T>> gt(x:T,y:T):Boolean //T的類型上界是Comparable<T>,即告訴編譯器,類型參數T代表的都是實現了Comparable接口的類,這樣等於告訴編譯器,它們都實現了CompareTo方法。
如果沒有上界聲明,則無法直接使用CompareTo操作符
out T 等價於 ? extend T 指定類型參數的上界
in T 等價於 ? super T 指定類型參數的下界
九、文件I/O操作、正則表達式與多線程
Kotlin的序列化直接採用了Java序列化類的類型別名
internal typealias Serializable = java.io.Serializable
Kotlin中常用的文件讀寫API:
File.readText() //讀取該文件的所有內容作爲一個字符串返回
File.readLines() //讀取該文件的每一行內容,存入一個List返回
File.readBytes() //讀取文件所有內容以ByteArray的方式返回
File.writeText() //覆蓋寫入text字符串到文件中
File.writeBytes() //覆蓋寫入ByteArray字節流數組
File.appendText() //在文件末尾追加寫入text字符串
File.appendBytes() //在文件末尾追加寫入ByteArray字節流數組
9.1遍歷文件樹
遍歷文件樹:walk函數
下面的例子遍歷了指定文件夾下的所有文件
fun traverseFileTree(filename:String){
val f = File(filename)
val fileTreeWalk = f.walk() //調用walk()會返回一個FileTreeWalk對象
fileTreeWalk.iterator().forEach{
println(it.absolutePath)
}
}
9.2遞歸複製文件
複製該文件或者遞歸該目錄及其所有子文件到指定目錄下,如果指定路徑下的文件不存在,會自動創建
copyRecursively函數簽名:
fun File.copyRecursively{
target:File, //目標文件
overrite:Boolean = false //是否覆蓋
onError:(File,IOException) -> OnErrorAction = { _,exception -> throw exception}) //錯誤處理
}:Boolean
9.3網絡I/0
readBytes和readText
使用這兩個方法配合正則表達式實現網絡爬蟲功能:
根據URL獲取該URL的響應HTML函數:
fun getUrlContent(url:String):String{
return URL(url).readText(Charset.defaultCharset()) //獲取該URL的響應HTML文本
}
根據URL獲取該URL的響應比特數組函數
fun getUrlBytes(url:String):ByteArray{
return URL(url).readBytes //獲取該URL的響應ByteArray
}
把URL響應字節數組寫入文件中
fun writeUrlBytesTo(filename:String,url:String){
val bytes = URL(url).readBytes()
File(filename).writeBytes(bytes) //寫入文件
}
9.4正則表達式
構造Regex表達式
val r1 = Regex("[a-z]+") //創建一個Regex對象,匹配的正則表達式是[a-z]+
val r2 = "[a-z]+".toRegex() //直接使用Kotlin中給String擴展的toRegex函數
Regex函數:
mataces() //輸入字符串全部匹配
containMatchIn() //輸入字符串至少有一個匹配
matchEntrie() //輸入字符串全部匹配,返回一個匹配結果對象
replace() //把輸入字符串中匹配的部分替換成replacement的內容//把輸入字符串中匹配到的值,用函數transform映射之後的新值替換
find() //返回輸入字符串中第一個匹配的值
findAll() //返回輸入字符串中所有匹配的值MatchResult的序列
同步方法和塊:
synchronized不是Kotlin中的關鍵字,它替換爲@Sychronized註解
同樣的,Kotlin中沒有volalile關鍵字,但是有@Volatile註解
參考:《Kotlin從入門到進階實戰》 ——陳光劍