這是一個新文集,專門用于記錄學(xué)習(xí) Kotlin 時遇到的一些問題或者心得體會。由于個人水平有限,文章難免會有錯誤之處,望大佬不吝指教。
Kotlin 由來已久,在17年被谷歌納為了 Android 開發(fā)的一級語言,相必大家也多少有些許了解。在開發(fā)工作中,還沒能正式的將項(xiàng)目來使用 Kotlin 開發(fā),但是了解 Kotlin 顯然已是迫在眉睫了。
舊項(xiàng)目引入 Kotlin 開發(fā)
如果你原來的項(xiàng)目使用 Java 編寫的,可以完全放心,我們可以從現(xiàn)在開始每一個新文件都使用 Kotlin 來編寫,Kotlin 與 Java 是百分百的可互相操作的。我們完全可以在保持原項(xiàng)目結(jié)構(gòu)不便的情況下,來使用 Kotlin 進(jìn)行后續(xù)的開發(fā)。
1 引入 Kotlin
- 在項(xiàng)目級 build.gradle 文件中引入
buildscript {
ext.kotlin_version = '1.2.41'
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
- 在模塊級 build.gradle 文件中引入
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
- 同步項(xiàng)目,開始使用 Kotlin 開始開發(fā)
此時你新建一個 Activity 的話,AS 將提示你可以選擇使用 Kotlin 來進(jìn)行開發(fā);

2 開始開發(fā)
打開我們剛剛新建的這個 Activity,我們來看看 Kotlin 新建的頁面與 Java 文件有那些區(qū)別;
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
}
}
在 Kotlin 中申明類也是使用關(guān)鍵字 “class”,類之間的繼承使用 “:” ,注意如果被繼承的父類也是一個 Kotlin 類文件(.kt),那么這個父類必須要使用 open 關(guān)鍵字修飾。在 Kotlin 中類默認(rèn)狀況相當(dāng)于在 Java 中使用 final 修飾。
另外 Kotlin 支持 override 父類的成員變量,在父類中使用 open 關(guān)鍵字修飾的成員變量,在子類中可以使用 override 來重寫,這一點(diǎn)在 Java 中是不支持的。
由于我們在前面引入了 apply plugin: 'kotlin-android-extensions' ,這使得我們可以直接在 Activity 中使用控件的 id 來調(diào)用控件,而無需在采用聲明 - findViewById這種方式來實(shí)例化空間了。
例如我們在 xml 文件中放一個 TextView 控件:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/mTvText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="這是一個測試的文字"
android:textColor="#ff00ff"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
我們可以這樣在 Activity 中操作:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
var oldText = mTvText.text
var newText = "這是一個新的字符串"
mTvText.text=newText
}
上述代碼中,我們可以把 text 直接看成是 TextView 的一個公開的成員變量來使用,省去了 get/set 方法,這樣的例子在 Kotlin 中還有很多。
在 Kotlin 中,我們使用 var 來聲明一個可以讀寫的變量,使用 val 來聲明一個只讀變量。使用 val 聲明的變量,在初始化其值后不允許再次賦值。與 Java 不同的是,在 Kotlin 中申明一個變量不一定需要顯示聲明,Kotlin 可以自動的推斷出一個變量的類型,一個變量在類型確定之后不可以修改其類型。
var a: String = "" //顯示聲明 a 為 String 類型
var b=1 //自動推斷出 b 為 int
空安全
Kotlin 是默認(rèn)空安全的,除非在變量在聲明時使用 “?” 來標(biāo)注該變量可以為 null,否則所有變量默認(rèn)都是不可為 null,這樣可以最大程度的避免空指針異常。
var nullString: String? = null
通常我們在 Android 編程時,都會在類中事先聲明大量的變量,這些變量并不是都能在聲明時初始化,往往很多都是在 Activity 的聲明周期里完成的初始化。
但是在 Kotlin 中 你會發(fā)現(xiàn)有這種情況:

面對這種情況有兩種解決方法:
- 在
init代碼塊中初始化
var d:String
init {
d = "init"
}
- 使用
lateinit和lazy進(jìn)行延時初始化
lateinit用于 var 變量,lazy用于 val 只讀變量。
lateinit 延時初始化
空判斷
如果我們已經(jīng)顯式的聲明了一個變量可以為 null,那么在對這個變量進(jìn)行操作時,我們需要對改變了進(jìn)行空判斷,這一點(diǎn) Java 中也是一樣的。


顯而易見的,如果是 Java 代碼,運(yùn)行到此處的時候回報(bào)空指針異常,導(dǎo)致程序崩潰。
在Kotlin 中我們有多種方式來進(jìn)行空判斷:
- 傳統(tǒng)的 Java 式在條件中顯示檢查 null
fun print(){
var nullString: String? = null
if (nullString != null) {
println(nullString.length)
} else {
println("empty")
}
}
- 使用安全調(diào)用 “?.” 操作符
?. 安全調(diào)用操作符
使用該操作符時,如果對象為 null,則不會調(diào)用該對象的方法,并且返回 null,不為 null 則正常執(zhí)行
相當(dāng)于:
if(nullString == null){
return null
}else{
return nullString.lenght
}
- Elvis 貓王操作符 “?:”
當(dāng)我們有一個可空的引用 r 時,我們可以說“如果 r 非空,我使用它;否則使用某個非空的值 x”,如下(注意:這是 kotlin 特有的操作,相當(dāng)于 Java 中的三元操作符,但是 Kotlin 更為強(qiáng)大允許將 if - else 語句塊中最后一句當(dāng)成返回值返回):
val l: Int = if (b != null) b.length else -1
我們可以將它簡化為:
val l = b?.length ?: -1

可以看出,變量 b 直到運(yùn)算的一刻才轉(zhuǎn)型成字符型,這一操作在 Java 中是不可以的。需要注意的是,如果一個對象被聲明為可能為空,那么除非是采取方法1在條件中判斷 null,否則我們必須采用安全調(diào)用的方式。
4.使用 “!!” 運(yùn)算符
上述的幾種方式都是讓我們?nèi)绾伪苊饪罩羔槷惓#袝r我們需要在代碼里拋出這個異常,這一操作需求在 Java 里是不需要的,因?yàn)橹灰覀儧]有做空判斷,代碼執(zhí)行到此處如果對象為空,必然會 NPE (NullPointerException)。但是在 Kotlin 里一個被聲明為可空的變量是不能直接被調(diào)用的(見上文),這時我們就需要 “!!” 操作符,這個操作符可以使編譯器忽略此處的空檢查,當(dāng)對象為空時,運(yùn)行到此處會 NPE。

上述的四種方法就是我們在 Kotlin 中最常用的四種空判斷方式,下面我們來總結(jié)一下:
- 當(dāng)我們需要一個常規(guī)的空判斷時,使用方法1;
- 當(dāng)我們需要在對象為空時,表達(dá)式結(jié)果為空,使用方法2;
- 當(dāng)我們需要在對象為空時,返回另一個表達(dá)式或者值,使用方法3;
- 當(dāng)我們需要在對象為空時能正常的拋出 NPE,使用方法4;
最后給大家看一個在 Kotlin 中才能使用的騷操作:


