Kotlin Tutorial(8)字串 << 前情
在許多資訊技術都可以看到「null」,例如資料庫與Java。在設計應用程式的時候,程式設計師通常也會把null加入設計的邏輯,例如使用null值表示沒有符合查詢條件的資料。不過null卻很容易造成應用程式發生錯誤,以Java程式設計語言來說,就是發生NullPointerException後讓應用程式中斷與結束。
null
null值在資訊技術通常表示一個「沒有任何資料的值」:
- 一個可以指定給物件變數的值,表示這個物件變數沒有任何對應的資料。對Java程式設計語言來說,null值表示沒有參考值(reference)對應到一個實體(instance)。
- 在Java程式設計語言,除了基本資料型態(primitive)以外,物件變數都可以指定null值,稱為null reference。
- 一個指定為null值的物件變數,在使用它的時候(例如呼叫函式),會發生執行時期例外,如果沒有執行額外的控制,就會導致程式中斷並結束。
例如下面的Java程式片段:
String name = "Simon";
System.out.println(name.toUpperCase());
// 顯示:SIMON
// ...
name = null;
System.out.println(name.toUpperCase());
// 發生例外:
// Exception in thread "main" java.lang.NullPointerException
為了預防錯誤的發生,必須在程式碼執行必要的判斷,例如下面的Java程式片段:
String name = "Simon";
// ...
name = null;
// 判斷變數不是null值後,再執行後續的工作
if (name != null) {
System.out.println(name.toUpperCase());
}
Sir Charles Antony Richard Hoare,通常被稱為Tony Hoare或C. A. R. Hoare,是英國的計算機科學家。在2009年的一次會議,他為發明null reference而道歉。下面是原文翻譯後的內容:
我稱之為十億美元的錯誤。我在1965年發明null reference。當時,我正在設計第一個物件導向程式設計語言(ALGOL W)的綜合型態系統。 我的目標是確保所有物件的使用都應該是絕對安全的,並且由編譯器自動執行檢查。但是,我無法抗拒使用null reference的誘惑,因為它很容易實現。這導致了無數的錯誤,漏洞和系統崩潰,這可能在過去四十年中造成了十億美元的痛苦和破壞。
原文請參考:Tony Hoare #Apologies and retractions。
不允許指定null值的變數
Kotlin為了預防null造成的問題,而且簡化應用程式的設計,在宣告物件變數的時候,預設的作法是不允許指定null值給物件變數:
// 使用一般的語法宣告字串物件變數
var name: String = "Simon"
// 編譯錯誤,不可以指定null值
//name = null
// 不可以指定null值的變數,可以直接呼叫函式
// 也不會因為null值發生例外
println(name.toUpperCase())
不允許指定null值的字串變數,可以直接呼叫各種功能的函式:
var name3: String = "Simon"
// 判斷變數是否為空白
println("name3 isBlank: ${name3.isBlank()}")
// 判斷變數是否沒有字元
println("name3 isEmpty: ${name3.isEmpty()}")
可以指定null值的變數
如果因為特殊的需求,或是為了跟Java應用程式的相容性,你也可在宣告物變數的時候,讓它可以指定null值。不過為了預防因為null造成的例外,Kotlin針對允許null值的變數設計了一些作法:
// 宣告可以指定null值的變數
var name2: String? = "Mary"
// 可以指定null值
name2 = null
// 編譯錯誤,因為可以指定null值的變數
// 不允許直接呼叫函式
// println(name2.length)
// 如果先經過null值的判斷...
if (name2 != null) {
// 就可以直接使用
println(name2.length)
}
為了預防null造成的問題,還有簡化應用程式的設計,Kotlin為呼叫函式加入了特別的作法:
// 宣告可以指定null值的變數
var name2: String? = "Mary"
// 必須在變數名稱後面多一個問號
// 變數是null值的時候,不會發生例外,不過會傳回null
println(name2?.length)
name2 = "Sam"
// 變數不是null值的時候,顯示呼叫函式執行後的結果3
println(name2?.length)
name2 = null
// 在變數名稱後面加入兩個驚嘆號(!!)
//println(name2!!.length)
// 發生例外
// Exception in thread "main" kotlin.KotlinNullPointerException
呼叫函式的時候,在變數名稱後面加入兩個驚嘆號(!!),這樣的作法在變數是null值的時候,會造成應用程式因為例外而中斷。在一般的情況下,都不應該使用這樣的作法。
允許指定null值的字串變數,在呼叫各種功能函式的時候,就會有一些限制:
var name: String? = null
// 判斷變數是否為null或空白
println("name.isNullOrBlank(): ${name.isNullOrBlank()}")
// 判斷變數是否為null或沒有字元
println("name.isNullOrEmpty(): ${name.isNullOrEmpty()}")
// 編譯錯誤,不可以直接呼叫
//println("name.isBlank(): ${name.isBlank()}")
//println("name.isEmpty(): ${name.isEmpty()}")
println("name.isBlank(): ${name?.isBlank()}")
println("name.isEmpty(): ${name?.isEmpty()}")
如果應用程式需要判斷變數是否為null值,根據判斷結果執行不同的工作,例如下面的程式片段:
val length: Int
if (name2 != null)
length = name2.length
else
length = 0
println("length: $length")
上面的應用可以使用Kotlin提供的「?:」運算式:
// 如果name2是null值,就使用「?:」後面的值
println( name2?.length ?: 0 )
陣列與元素
物件儲存在陣列的時候,除了變數本身外,元素也有null值的問題。你可以根據應用程式的需求,使用下面程式片段的作法宣告陣列:
// 變數與元素都不可以指定null值
var names3: Array<String> = arrayOf("One", "Two", "Three")
// 變數不可以指定null值,但是元素可以
var names4: Array<String?> = arrayOf("One", "Two", "Three")
// 變數與元素都可以指定null值
var names5: Array<String?>? = arrayOf("One", "Two", "Three")
一個變數與元素都不可以指定null值的陣列,在使用的時候,都可以直接呼叫函式執行需要的工作:
// 變數與元素都不可以指定null值
var names = arrayOf("Simon", "Mary", "Sam")
// 編譯錯誤,變數不可以指定null值
// names = null
// 編譯錯誤,元素不可以指定null值
// names[0] = null
for (element in names) {
print("${element.length}\t")
}
如果陣列變數不可以指定null值,但是因為應用程式的需求,讓元素可以指定null值,使用陣列元素的時候,就必須遵守相關的規則:
// 變數不可以指定null值,但是元素可以
val names2 = arrayOf<String?>("Simon", "Mary", "Sam")
// 元素可以指定null值
names2[0] = null
for (element in names2) {
// 編譯錯誤,因為元素可以指定null值
// print("${element.length}\t")
print("${element?.length}\t")
}
下一步
完成Kotlin程式設計語言的基礎,還有認識基本的物件與null值以後,接下來準備開始認識物件導向程式設計,為應用程式設計需要的類別。
相關的檔案都可以在GitHub瀏覽與下載。
http://github.com/macdidi5/Kotlin-Tutorial
後續 >> Kotlin Tutorial(10)函式
|