第一章 定義和目的
kotlin的主要特征
- 目標(biāo)平臺:服務(wù)器端踪区,Android及任何Java運行的地方
- 靜態(tài)類型
- 函數(shù)式和面向?qū)ο?/li>
- 免費且開源
小結(jié)
- fun關(guān)鍵字用來聲明函數(shù)热芹,val關(guān)鍵字和var關(guān)鍵字分別用來聲明只讀變量和可變變量
- 字符串模板幫助你避免繁瑣的字符串連接互亮。在變量名稱前加上$前綴或者用${}包圍一個表達(dá)式,來把值注入到字符串中
- 值對象類在Kotlin中以簡潔的方式表示
- 熟悉的if現(xiàn)在是帶返回值的表達(dá)式
- when表達(dá)式類似于Java中的switch但功能更強大
- 在檢查過變量具有某種類型之后不必顯示地轉(zhuǎn)換它的類型:編譯器使用智能轉(zhuǎn)換自動幫你完成
- for,while畏纲,和do-while循環(huán)與Java類似,但是for循環(huán)現(xiàn)在更加方便春缕,特別是當(dāng)你需要迭代map的時候盗胀,又或是迭代集合需要下標(biāo)的時候。
- 簡潔的語法1..5會創(chuàng)建一個區(qū)間锄贼,區(qū)間和 數(shù)列允許Kotlin在for循環(huán)中使用統(tǒng)一的語法和同一套抽象機制票灰,并且還可以使用in運算符和!in運算符來檢查值是否屬于某一個區(qū)間
- Kotlin中的異常處理和Java非常相似咱娶,除了Kotlin不要求你聲明函數(shù)可以拋出的異常
第三章 函數(shù)的定義和調(diào)用
創(chuàng)建集合
val set = hashSetOf(1,2,3,4,5,6)
val list = arrayListOf(1,2,3,4,5)
val map = mapOf(1 to "one",2 to "two",3 to "tree")
kotlin沒有自己的集合類米间,而是采用的標(biāo)準(zhǔn)的Java集合類,kotlin可以更容易地與Java代碼交互膘侮,可以調(diào)用變量的JavaClass查看變量的類型屈糊。
讓函數(shù)更好的調(diào)用
/**
* 集合按規(guī)定格式轉(zhuǎn)換為字符串
*/
fun <T> joinToString(collection: Collection<T>,
separator: String,
prefix: String,
postfix: String
) :String{
val result = StringBuilder(prefix)
for ((index,element) in collection.withIndex()){
if (index>0) result.append(separator)
result.append(element)
}
result.append(postfix)
return result.toString()
}
調(diào)用println(joinToString(set,",","[","]"))
結(jié)果:[1,2,3,4,5,6]
joinToString是kotlin集合類默認(rèn)的方法,可以直接用
命名參數(shù)
可以顯示地表明一些參數(shù)的名稱琼了,如果在調(diào)用一個參數(shù)時逻锐,指明了一個參數(shù)的名稱,為了避免混淆雕薪,那它以后的所有參數(shù)都需要標(biāo)明名稱昧诱。
默認(rèn)參數(shù)值
當(dāng)使用常規(guī)的調(diào)用語法時,必須按照函數(shù)聲明中定義的參數(shù)順序來給定參數(shù)所袁,可以省略的只有排在末尾的參數(shù)盏档。如果使用命名參數(shù),可以省略中間的一些參數(shù)燥爷,也可以以你想要的任意順序只給定你需要的參數(shù)蜈亩。
fun main(args: Array<String>) {
println(joinToString(set,",","[","]"))
println(joinToString(set,",","["))
println(joinToString(set,","))
println(joinToString(set,postfix = "}",prefix = "{"))
}
/**
* 集合按規(guī)定格式轉(zhuǎn)換為字符串
*/
fun <T> joinToString(collection: Collection<T>,
separator: String = ",",
prefix: String = "[",
postfix: String = "]"
) :String{
val result = StringBuilder(prefix)
for ((index,element) in collection.withIndex()){
if (index>0) result.append(separator)
result.append(element)
}
result.append(postfix)
return result.toString()
}
消除靜態(tài)工具類:頂層函數(shù)和屬性
在Kotlin中懦窘,不需要去創(chuàng)建一個類作為靜態(tài)函數(shù)的容器,相反稚配,可以把這些函數(shù)直接放在代碼文件的頂層畅涂,不用從屬于任何的類。這些放在 文件頂層的函數(shù)依然的包內(nèi)的成員道川,如果你需要從包外訪問它午衰,則需要import,但不需要額外包一層。Kotlin編譯生成的類的名稱冒萄,對應(yīng)于包含函數(shù)的文件的名稱臊岸,這個文件中的所有頂層函數(shù)編譯為這個類的靜態(tài)函數(shù),因為Java要調(diào)用這個函數(shù)的時候和調(diào)用靜態(tài)函數(shù)一樣
改變包含Kotlin頂層函數(shù)的生成的類的名稱尊流,需要為這個文件添加@JvmName的注解扇单,將其放到這個文件的開頭,位于包名的前面奠旺。
頂層屬性
和函數(shù)一樣,頂層屬性和頂層函數(shù)一樣可以放在文件的頂層施流,和其它屬性一樣响疚,val只有一個getter,var對應(yīng)一對getter和setter,如果想要把一個常量public static final的屬性暴露給Java,可以用const來修飾它
給別人的類添加方法:擴展函數(shù)和屬性
fun String.lastChar(): Char = this.get(this.length - 1)
擴展函數(shù)就是一個類的成員函數(shù)瞪醋,不過定義在類的外面忿晕,所要做的就是把要擴展的類或者接口的名稱,放到即將添加的函數(shù)前面银受,這個類的名稱稱為接收者類型践盼;用來調(diào)用這個擴展函數(shù)的那個對象,叫做接收對象宾巍。
import string.lastChar
fun main(args: Array<String>) {
println("Hello".lastChar())
}
導(dǎo)入函數(shù)還可以用as改名
import string.lastChar as last
fun main(args: Array<String>) {
println("Hello".last())
}
Java中調(diào)用kotlin的函數(shù)就是調(diào)用kotlin生成的類文件的靜態(tài)方法咕幻,比如剛才那個方法如果聲明在StringUtil.kt的文件中,java中
char c = StringUtilkt.lastChar("Java")
擴展函數(shù)無非就是靜態(tài)函數(shù)的一個高效的語法糖顶霞,可以使用具體的類型作為接收者類型肄程,而不是一個類。
不可重寫擴展函數(shù)
擴展函數(shù)并不是類的一部分选浑,它是聲明在類之外的蓝厌。
如果一個類的成員函數(shù)和擴展函數(shù)有相同的簽名,成員函數(shù)往往會被優(yōu)先調(diào)用古徒。
擴展屬性
擴展屬性提供了一種方法拓提,用來擴展類的API,可以用來訪問屬性隧膘,用的是屬性語法而不是函數(shù)的語法代态。
val String.lastChar: Char
get()=get(length-1)
調(diào)用
fun main(args: Array<String>) {
println("Hello".lastChar)
}
可變屬性
var StringBuilder.builderLastChar: Char
get()=get(length-1)
set(value :Char) = this.setCharAt(length-1,value)
就是一個set和一個get
處理集合:可變參數(shù)寺惫,中綴調(diào)用和庫的支持
可變參數(shù):讓函數(shù)支持任意數(shù)量的參數(shù)
vararg 修飾可變參數(shù),數(shù)組變量前加*表示展開這個集合
fun zhankaiArgs(vararg args: Int){
for ( e in args){
print("$e ")
}
}
fun main(args: Array<String>) {
val list = intArrayOf(1,3,4,5,6)
zhankaiArgs(1,*list)
}
鍵值對的處理:中綴調(diào)用和解構(gòu)聲明
1.to("one) //一般to函數(shù)的調(diào)用
1 to "one" //使用中綴符號調(diào)用to函數(shù)
中綴調(diào)用可以與只有一個參數(shù)的函數(shù)一起使用胆数,無論是普通的函數(shù)還是擴展函數(shù)肌蜻。要允許使用中綴符號調(diào)用函數(shù),需要使用infix修飾符來標(biāo)記它必尼。
infix fun Any.to(other:Any) = Pair(this,other)
//使用
val pair = 2 to "two"
print(pair)
//mapOf的聲明
public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V>
結(jié)果:(2蒋搜,two),一個Pair對象就是一個鍵值對
字符串和正則表達(dá)式的處理
//分割表達(dá)式
val s="12.345-6.A".split("\\.|-".toRegex())//用正則表達(dá)式
val s2="12.345-6.A".split(".","-")//指定多個分隔符
//print結(jié)果都為[12, 345, 6, A]
使用String的擴展函數(shù) 解析文件路徑string
fun parsePath(path:String){
val directory = path.substringBeforeLast("/")
val fullName = path.substringAfterLast("/")
val fileName = fullName.substringBeforeLast(".")
val extension = fullName.substringAfterLast(".")
print("directory: $directory fullName: $fullName fileName: $fileName extention: $extension")
}
結(jié)果:
directory: H:/Users/yorl/kotlin-book fullName: chater.adoc fileName: chater extention: adoc
使用正則表達(dá)式解析
fun regexParsePath(path:String){
val regex="""(.+)/(.+)\.(.+)""".toRegex()
val matchResult = regex.matchEntire(path)
if (matchResult!=null){
val (directory,fileName,extention) = matchResult.destructured
println("directory: $directory fileName: $fileName extention: $extention")
}
}
在三重引號字符串中,不需要對任何字符串進(jìn)行轉(zhuǎn)義判莉,包括反斜線
讓代碼更整潔:局部函數(shù)和擴展
如果一個函數(shù)內(nèi)有一些重復(fù)操作豆挽,可以使用局部函數(shù),局部函數(shù)可以訪問所在函數(shù)中的所有參數(shù)和變量券盅。
fun saveUser(user:User){
fun validate(value:String, fieldName:String){
if (value.isEmpty()){
throw IllegalArgumentException(
"Can't save user ${user.id}: empty $fieldName"
)
}
}
validate(user.name,“Name”)
validate(user.name,“Address”)
//...
}
進(jìn)一步改進(jìn)
fun saveUser(user:User){
user.validateBeforeSave()
//...
}
fun User.validateBeforeSave(){
fun validate(value :String, fieldName :String){
if (value.isEmpty()){
throw IllegalArgumentException(
"Can't save user ${id} : empty $fieldName"
)
}
}
validate(name,"Name")
validate(adress,"Address")
}
可以看到擴展函數(shù)也可以用局部函數(shù)
小結(jié)
- kotlin沒有定義自己的集合類帮哈,而是在Java集合類的基礎(chǔ)上提供了更豐富的API。
- Kotlin可以給函數(shù)參數(shù)定義默認(rèn)值锰镀,這樣大大降低了重載函數(shù)的必要性娘侍,而且命名參數(shù)讓多參數(shù)函數(shù)的調(diào)用更加易讀
- Kotlin可以用擴展函數(shù)和屬性來擴展任何類的API,包括在外部庫中定義的類泳炉,而不需要修改其源代碼憾筏,也沒有運行時開銷
- 中綴調(diào)用提供了單個參數(shù)的,類似調(diào)用運算符方法的簡明語法
- Kotlin為普通字符串和正則表達(dá)式都提供了大量的方便字符串處理的函數(shù)花鹅。
- 三重引號的字符串提供了一種簡潔的方式氧腰,解決了原本在java中需要啰嗦的的轉(zhuǎn)義和字符串連接的問題
- 局部函數(shù)幫助保持代碼整潔的同時,避免重復(fù)
第四章 類刨肃,對象和接口
Kotlin的類和接口與Java的類和接口有一點區(qū)別古拴,例如,接口可以包含屬性聲明真友,與Java不同黄痪,Kotlin的聲明默認(rèn)是final和public的,此外锻狗,嵌套的類默認(rèn)并不是內(nèi)部類满力,它們并沒有包含對其外部類的隱式引用。
4.1 定義類繼承結(jié)構(gòu)
kotlin的接口
- 使用interface關(guān)鍵字聲明接口.
- Kotlin在類名后面使用冒號代替了Java中的extends和implements關(guān)鍵字轻纪,和Java一樣油额,一個類可以實現(xiàn)任意多個接口,但是只能繼承一個類刻帚。override修飾符用來標(biāo)注被重寫的父類或者接口中的方法和屬性潦嘶,并且是kotlin中的override修飾符是強制要求的。
- 接口的方法可以有一個默認(rèn)實現(xiàn)
interface Clickable{
fun click()
fun showOff()=println(" I'm clickable!")
}
interface Focusable{
fun setFocus(b: Boolean){
println("I ${if(b) "got" else "lost"} focus.")
}
fun showOff() = println("I'm focusable!")
}
class Button:Clickable,Focusable{
override fun click() {
println("I was Clicked!")
}
/**
* 兩個接口有同樣的函數(shù)下崇众,必須自己顯示實現(xiàn)
*/
override fun showOff() {
super<Clickable>.showOff()//可調(diào)用接口的默認(rèn)實現(xiàn)
super<Focusable>.showOff()
}
//或者
// override fun showOff() = super<Clickable>.showOff()
}
open,final和abstract修飾符:默認(rèn)為final
java的類和方法默認(rèn)是open的掂僵,而kotlin默認(rèn)是final的航厚,如果想允許創(chuàng)建一個類的子類,需要使用open修飾符來標(biāo)示這個類锰蓬,此外幔睬,需要給每一個可以被重寫的屬性或方法添加open修飾符
open class RichButton:Clickable{//這個類是open的,其它類可以繼承它
fun desable(){}//這個函數(shù)是final的芹扭,不能在子類重寫它
open fun animate(){}//這個函數(shù)是open的麻顶,可以在子類重寫它
override fun click() {//這個函數(shù)重寫了一個open函數(shù)并它本身同樣是open的
println("I'm clicked!")
}
final override fun showOff() =super.showOff()//聲明這個函數(shù)在子類不可重寫
}
注意:如果重寫了一個基類或者接口的成員,重寫了的成員同樣默認(rèn)是open的舱卡,如果想改變這一行為辅肾,阻止你的類的子類重寫你的子類,可以顯示地標(biāo)注為final
abstract 聲明抽象類轮锥,這種了不能實例化矫钓,一個抽象類通常包含一些沒有實現(xiàn)并且必須在子類重寫的抽象成員,抽象成員始終是open的舍杜,所有不需要顯示的使用open修飾符
abstract class Animated{//類是抽象的新娜,不能被實例化,所有是open的
abstract fun animate()//函數(shù)的抽象的既绩,必須被子類實現(xiàn)
open fun stopAnimating(){}//抽象類的非抽象函數(shù)并不是默認(rèn)open的杯活,但是可以標(biāo)注為open的
fun animateTwice(){}
}
修飾符 | 相關(guān)成員 | 評注 |
---|---|---|
final | 不能被重寫 | 類中成員默認(rèn)使用 |
open | 可以被重寫 | 需要明確的表明 |
abstract | 必須被重寫 | 只能在抽象類中使用;抽象成員不能有實現(xiàn) |
override | 重寫父類或接口中的成員 | 如果沒有使用final,重寫的成員默認(rèn)是開放的 |
可見性修飾符:默認(rèn)為public
Javaz中的默認(rèn)可見性--包私有熬词,在Kotlin中并沒有使用,Kotlin只把包作為在命名空間里組織代碼的一種方式使用吸重,并沒有將其用作可見性控制互拾。Kotlin提供了一個新的修飾符,internal,表示只在模塊內(nèi)部可見嚎幸,一個模塊就是一組一起編譯的kotlin文件
修飾符 | 類成員 | 頂層聲明 |
---|---|---|
public(默認(rèn)) | 所有地方可見 | 所有地方可見 |
internal | 模塊中可見 | 模塊中可見 |
protected | 子類中可見 | - |
private | 類中可見 | 文件中可見 |
內(nèi)部類和嵌套類:默認(rèn)是嵌套類
類A在另一個類B中聲明 | 在Java中 | 在Kotlin中 |
---|---|---|
嵌套類(不存儲外部類的引用) | static class A | class A |
內(nèi)部類(存貯外部類的引用) | class A | inner Class A |
/**
* 內(nèi)部類
*/
class Outer{
inner class Inner{
fun getOuterReference(): Outer = this@Outer //獲取外部類的引用
}
}
密封類:定義受限的類繼承結(jié)構(gòu)
**
* 密封類颜矿,Expr2包括了所有的子類
*/
sealed class Expr2{
class Num(val value: Int): Expr2()
class Sum(val left: Expr2,val right: Expr2):Expr2()
}
fun eval2(e: Expr2):Int =
when(e){//when 表達(dá)式涵蓋了所有可能的情況,所以不在需要else分支
is Expr2.Num -> e.value
is Expr2.Sum -> eval2(e.left)+ eval2(e.right)
}
4.2 聲明一個帶非默認(rèn)構(gòu)造方法或?qū)傩缘念?/h2>
初始化類:主構(gòu)造函數(shù)和初始化語句塊
//class User3(val nickName:String) //"val"意味著相應(yīng)的屬性會用構(gòu)造方法的參數(shù)來初始化
class User3 constructor(_nickName: String){//帶一個參數(shù)的主構(gòu)造方法
val nickName: String
init {
nickName = _nickName //初始化語句塊
}
}
如上圖是兩種聲明類的方式嫉晶,方式二用到了關(guān)鍵字constructor和init骑疆,constructor用來開始一個主構(gòu)造方法或從構(gòu)造方法的聲明,init用來引入一個初始化語句塊替废,這種語句快包含了在類被創(chuàng)建時執(zhí)行的代碼箍铭,并會與主構(gòu)造方法一起使用。
//class User3(val nickName:String) //"val"意味著相應(yīng)的屬性會用構(gòu)造方法的參數(shù)來初始化
class User3 constructor(_nickName: String){//帶一個參數(shù)的主構(gòu)造方法
val nickName: String
init {
nickName = _nickName //初始化語句塊
}
}
如上圖是兩種聲明類的方式嫉晶,方式二用到了關(guān)鍵字constructor和init骑疆,constructor用來開始一個主構(gòu)造方法或從構(gòu)造方法的聲明,init用來引入一個初始化語句塊替废,這種語句快包含了在類被創(chuàng)建時執(zhí)行的代碼箍铭,并會與主構(gòu)造方法一起使用。
當(dāng)然再簡化可以這樣:
class User5 constructor(_nickName: String){//帶一個參數(shù)的主構(gòu)造方法
val nickName =_nickName
}
構(gòu)造方法:用不同的方式來初始化父類
//用不同的方法來初始化父類
class Context
class Attribute
open class View {
protected var name: String="View"
val context: Context
val attr: Attribute?
constructor(ctx: Context)
:this(ctx,null)
constructor(ctx: Context,art: Attribute?){
context=ctx
attr=art
}
override fun toString(): String {
return "View(name='$name')"
}
}
class MyButton: View{
constructor(ctx: Context)
:this(ctx,null){
name="MyButton"
}
constructor(ctx: Context,art: Attribute?)
:super(ctx=ctx,art = art)
override fun toString(): String{
return "Mybutton(name=${name})"
}
}
實現(xiàn)在接口中聲明的屬性
// 實現(xiàn)在接口中聲明的屬性
interface UserInterface{
val nickName: String //聲明屬性
}
//只填寫了昵稱的用戶
class PrivateUser(override val nickName: String):UserInterface //主構(gòu)造方法屬性
//提供了email進(jìn)行注冊的用戶
class SubscribingUser(val mail: String):UserInterface{
override val nickName: String
get() = mail.substringBefore("@")//自定義屬性及getter
}
//共享了Facebook賬戶的用戶
class FacebookUser(val accountId: Int):UserInterface{
override val nickName = getFacebookName(accountId)//屬性初始化
/**
* 由id獲取用戶名
*/
fun getFacebookName(accountId: Int): String{
return "facebook:"+accountId
}
}
通過getter和setter訪問支持字段
/**
*通過getter或setter訪問支持字段
*/
class User2(val name: String){
var address: String ="unspecified"
set(value) {
println("""
Address was changed for $name:
"$field" -> "$value".""".trimIndent())
field=value
}
}
修改訪問器的可見性
/**
* 修改訪問器的可見性
*/
class LengthCounter{
var counter: Int =0
private set //不能在類外部修改這個屬性
fun addWord(word: String){
counter +=word.length
}
}