iT邦幫忙

2021 iThome 鐵人賽

DAY 20
1
Software Development

溫柔學姐的Kotlin補課/教學系列 第 20

在程式裡避開踩雷:安全引用空虛值、例外處理和延後、惰性初始化 Null Safety, Exception, lateinit, lazy

  • 分享至 

  • xImage
  •  

「哇,原來研究室是這樣的啊。」詩憶東張西望。

「是呀,這裡是大家辦公的地方,實驗的機器不在這裡,所以妳可以放心坐下來。」唯心拉開身邊的椅子,回頭繼續寫報告。

詩憶下午的時間有空堂,想要找個安靜的地方自習,但系計中(系上的計算機中心,提供多台公共電腦和舒適的冷氣)和圖書館都人滿為患,回寢室又太遠。於是傳訊息問學姐還有沒有適合的地方,沒想到學姐回答可以去她的研究室。

詩憶坐下後,拿出筆電開始研究講義上的Nullable範例,她在宣告變數的類別後面加上問號。

fun main() {
    var drink : Drink?
    println(drink)
}

IDE馬上吐出紅字錯誤Variable 'drink' must be initialized

「妳忘記初始化了。」唯心幾乎是和IDE同時反應。

「喔,學姐妳有在看啊。」詩憶害羞地用手指搔搔臉。「我可以順便問問這個字怎麼發音嗎?」

null在台灣還滿盛行唸成『怒喔』,但如果要和英美人士交流的話,音標是『nʌl』,發音接近NO。基本上溝通就是要讓對方聽懂,所以建議兩種念法都記下來。null是一個特殊的值,當變數被設定成null這個空虛值,就代表這個變數失去原類別的所有能力,只剩下Any支援的函式,被扔在記憶體hashCode為0的位置,所有的null變數都是被扔在那,所以通常是拿來解放原本佔據的記憶體的。所以?.後面的函式和屬性只在變數非null的時候才會去呼叫。」唯心一邊幫她修正程式一邊說。

fun main() {
    var drink : Drink? = null
    println(drink)
    println(drink?.name)
}

IDE這次沒有紅字,印出兩行null

「不能延後初始化嗎?」詩憶問。

唯心注意到螢幕的髒污,拿出拭鏡布擦乾淨了才回答。

「可以,不過valvar延後初始化的方法不同唷,var是在前面加上lateinitval是在後面加上by lazy。意義上不太一樣,lateinit是希望開發者能自發在用到這個變數前初始化,如果在用到變數時還是沒有初始化的話就會拋出UninitializedPropertyAccessException錯誤;而by lazy則事先準備好初始化的方法,程式會在用到變數的前一刻才初始化。」

fun main() {
    lateinit var drink : Drink
    drink = Drink("奶茶", 50)
    println(drink)
}
fun main() {
    val drink : Drink by lazy {
        println("initial")
    	Drink("奶茶", 50)//初始化的部分要放在lazy裡的最後一行唷
    }
    println(drink)
}

「之前練習四則運算的時候就想問,Exception到底是什麼啊?」

唯心乾脆蓋上筆電,專心回答詩憶。

throw Exception歸屬於Nothing類別,是一種中斷程式、放置安全閘的手段,比如說,程式還沒設計完的時候通常會放個TODO,如果有誰不小心呼叫了這個未完成的地方,就會收到錯誤。之前的四則運算則是擋住不支援的字元符號,而有些Exception是系統拋出的,可以的話盡量不要踩到那些坑,像是很有名的NPE指的就是NullPointerException,當程式企圖操作已經null化的變數就會引爆。因為Kotlin有預先檢查,相對於同生態系的Java程式語言來說Null Safety程度比較高,但是也因為同生態系內的程式語言可以互相使用對方的函式庫,所以還是有可能踩到對方的坑。不過,也不能說Kotlin自己就不會發生NullPointerException,如果刻意呼叫throw NullPointerException或是使用!!無視null狀態,會爆炸的還是會爆炸。」

fun 飲料優惠活動() {
    TODO("總有一天要完成")
}

fun notSupport(char: Char): Nothing {
    throw IllegalArgumentException("Not Support ${char}!")
}

fun main() {
    var drink : Drink? = null
    println(drink)
    println(drink!!.name)//會爆炸
}

「遇到Exception只能爆炸嗎?不會吧?」詩憶懷疑。

唯心點頭。「如妳猜測的,對於在預期中可以包容的Exception,能用try-catch包起來不讓它爆炸。」

fun main() {
    try {
        notSupport('#')
    } catch (e: IllegalArgumentException) {
        println(e.message)
    }
}

程式順利執行,印出一般顏色的Not Support #!


上一篇
想要彈性類別嗎,讓類別當參數吧:泛型 Generics
下一篇
餅乾的危險性:函式庫 Library
系列文
溫柔學姐的Kotlin補課/教學31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言