Kotlin 線上讀書會 筆記 (一)變數、常數、條件式

Evan Chen
Evan Android Note
Published in
10 min readMar 24, 2020

雖然工作有在用Kotlin開發Android App,但一直沒有很深入。趁著這次參加Kotlin Taipei 范聖佑 主辦的線上讀書會,就照著書本及讀書會的進度,重新來檢視一下自已的Kotlin的認知吧。

這次讀書會用的書是:
英文版:Kotlin Programming: The Big Nerd Ranch Guide
中文版:Kotlin 權威 2.0:Android 專家養成術

不管是讀書會還是線上課程,最好的學習方式就是結束之後寫筆記跟心得了。為期3個多月的讀書會,只要撐下去,Kotlin就更進步了!

本週的範圍是
第二章 變數、常數
第三章 條件式

第二章 變數、常數

在kotlin要宣告一個變數,可以使用varval。兩者的差異為val在賦值後就不能再修改。

用var宣告一個名為score的Int變數,並給值等於5。

var score : Int = 5

當宣告的型別與給值的型別不一樣時,IntelliJ 會馬上告訴你有錯誤。

用val宣告則代表這個變數一旦給值就不能再修改了。

val score: Int = 5

儘可能優先使用val

若你不確定變數以後會不會被修改,可以先使用val,等到需要修改時,再改為var。

按下Option + Enter 可改為var

反過來,如果宣告為var 卻沒有修改過變數的值,IntelliJ 也會建議你修改為val。

類別推斷

Kotlin 有個語言特色叫類別推斷。對於已宣告給值的變數,允許你省略型別的定義。

下例的給值已經可以表達變數score是Int,Intellj會給建議拿掉。

const

寫過Java的話,你可能會問那val 跟 final 是不是一樣的。在Kotlin,如果要等同Java 的final,則要使用const val。

const val STRING2 = "2"class Class1{
var string1 = "1"
}

const 只允許在top-level 和object 或 companion object 中宣告。所以const val 可見性為public final static。

我們就來看看const 在Kotlin編譯器產生的Java程式碼是怎麼樣。

Tools → Kotlin → Show Kotlin Bytecode

接著在Kotlin Bytecode點下Decompile,就可以看到用val 宣告的常數STRING2放宣告為 public static final。

public final class Class1Kt {
@NotNull
public static final String STRING2 = "2";
}
public final class Class1 {
@NotNull
private String string1 = "1";
@NotNull
public final String getString1() {
return this.string1;
}
public final void setString1(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.string1 = var1;
}
}

那麼在Android 你什麼時候會用const呢?

例如關閉一個Activity要回傳資料時,就會使用startActivityForResult,裡面的requestCode就會用const val 來宣告。


{
const val REQUEST_CODE_LOGIN = 1
...
private fun pickContact() {
startActivityForResult(intent, REQUEST_CODE_LOGIN)
}
}

基本型別

在Java ,型別有分為Reference Type與Primitive Type。例如int是Primitive Type,而Integer是Reference Type。

int a = 1;
Integer b = 1;

而在Kotlin沒有Primitive Type,一律使用大寫Reference Type的Int。這樣的好處是你就不用再去想要用哪一種,直接把大寫的用法都忘掉。

val a:Int = 1

如果你使用kotlin byteCode,就會看到其實在Java還是會轉成Primitive type int。

第三章 條件式

條件式的判斷,與Java或其他語言一樣,都是使用if做為條件式

if ( string == "A" ){
println("string is A")
}else if ( string == "B" ){
println("string is B")
}else{
println("string is else")
}

比較運算子

在if 的條件判斷式裡,有這些運算子來做比較。

< 小於
<= 小於等於
>大於
>= 大於等於
== 等於
!= 不等於
=== 相同參考
!== 不同參考

邏輯運算子

&& 且
|| 或
! 非
in 在某個range間

運算子權重

運算子是有優先順序的:

!
< ≤ > ≥
&&
||

即便運算子有優先順序,我們還是傾向一律用大括號括起來較易閱讀。

//不管權重怎麼樣,直接把你想要優先的括號起來,更容易解讀。
if ( (a == true && b == true) || c == false ){

}

if else 的簡化

if ( isCorrect == true)

簡化為

if (isCorrect)

同樣的 == false 也可以簡化

if (!isCorrect)

大括號可以被省略

//建議只用一行
if ( isCorrect) result = "Good"
//不要這樣使用,只有if下方的第一行會被視為if裡面的內容
if ( isCorrect)
result = "Good"

把if else 的結果給值到一個變數。

var result = if ( isCorrect) "Good" else "Bad"

range

你也可以在if裡條件式裡判斷一個值是不是在一個範圍內

if ( 1 in 1..10){

}

When

Kotlin 提供了另一種條件式的方式就是When。

if (type == "A") {
println("typeA")
} else if (type == "B") {
println("typeB")
} else if (type == "C") {
println("typeC")
}

改成

when (type) {
"A" -> println("typeA")
"B" -> println("typeB")
"C" -> println("typeC")
}

或是搭配in使用

when (age) {
in 1..12 -> println("小孩")
in 13..64 -> println("大人")
else -> println("長者")
}

在if 上按下Option + Enter 可轉換至when

if 的Style

對於if else 中該不該空格或換行,或許你可以不再糾結了,就直接使用Code -> Reformat Code ( Option + Command + L)格式化。如果你的團隊有固定的格式規範,你也可以在Preference -> Editor裡設定。

你還可以設定在Commit之前自動Reformat code。

我的習慣是把Reformat code的指令跟執行測試或執行App時透過Vim綁在一起,Vim可以參考這篇

反模式 Anti pattern

if else 用起來很簡單,但也很容易就寫出一些不好維護的程式碼。我們來看幾個範例。

下方程式碼使用了! 及isNotSuccess,很難一眼看出到底是成功要處理,還是不成功要處理。

if ( ! isNotSuccess ){}

另一個案例是需求如下:

買1本書 ⇒ 原價
買2本以上 ⇒ 9折
買3本以上 ⇒ 8折
買3本以上且超過1000元 ⇒ 7折

//作法1:Bad
if (books >= 2 && books < 3) {
//打9折
}
else if (books >=3 && totalPrice > 1000) {
//打7折
}else if (books >=3 && totalPrice < 1000) {
//打8折
}
//作法2:Good
if (books >= 2 && books < 3) {
//打9折
}
else if (books >=3 ){
if ( totalPrice > 1000) {
//打7折
}else{
//打8折
}
}

作法2是一個較好的選擇,先判斷書本≥3,再去判斷打7折或8折。這個寫法會更貼近你的需求,強調3本以上才有7或8折。

把else視為Exception

寫Android App時,有時Activity會需要依照intent接收的資料,當值等於A與B 做不同的事。

//作法1,把B處理放到else
val type = intent.getStringExtra(TYPE)
if ( type == "A"){
//A處理
}else {
//B處理
}
//作法2,A、B之後都視為Exception
val type = intent.getStringExtra(TYPE)
if ( type == "A"){
//A處理
}else if ( type == "B"){
//B處理
}else{
throw Exception("TYPE Error")
}

作法2是較好的處理方式,如果type被傳入C,應拋出Exception。

再看一個範例:檢查VIP會員
如果不是VIP會員 => Alert不是會員
如果會員過期 => Alert會員已過期
是VIP會員且未過期 => 開啟VIP會員頁面

作法1
if (是VIP會員) {
if (會員未過期) {
//開啟VIP頁面
} else {
//Alert('會員已過期')
}
} else {
//Alert('不是VIP會員')
}
作法2
if (不是VIP會員) {
//Alert('不是VIP會員')
} else if (會員已過期) {
//Alert('會員已過期')
} else {
//開啟VIP頁面
}

這個範例,對需求來說,最重要的事是開啟VIP頁面。作法1把這件事放在if的太裡面了。作法2是較好的作法,先一步一步檢查不合法,最後的else是開啟VIP頁面。

👊 給Android 初學者 的快速成長 線上課程

1️⃣ UI 進階實戰Material Design Component 讓你簡單做出效果超好的UI

2️⃣ 動畫入門到進階用動畫提升使用者體驗

3️⃣ 架構設計MVP、MVVM 讓你程式碼好維護

🆙 3堂組合包更划算 — Android 架構設計 + 動畫入門到進階 + UI 進階實戰

這週的筆記就到這裡結束。下一篇的主題是函式與匿名函式。

--

--