昨天介紹了 Class 、 Constructor 、 Properties 和 Extends ,那今天要繼續介紹各式各樣的類別,分別有
以及我個人滿愛用的 物件( Object )和 伴隨物件( Componion Object )
有時候為了保證型別安全,因此會採用 Enum 列出所有的值,開發時只能使用被列出的值,那 Kotlin 中要使用 Enum ,就需要把 enum
關鍵詞加在 Class 前面Enum classes
中也可以加上需要的 Properties 和 Functions ,這部份與 Java 都一樣
Example:
// Weather.kt
enum class Weather(private val weather: String) {
CLEAR("Clear"),
CLOUDY("Cloudy"),
RAIN("Rain"),
FOG("Fog"),
SNOW("Snow");
// 可以透過 enum 取得相對應的圖片
fun drawableID(): Int {
return when (this) {
CLEAR -> R.drawable.clear
CLOUDY -> R.drawable.cloudy
RAIN -> R.drawable.rain
FOG -> R.drawable.fog
SNOW -> R.drawable.snow
else -> R.drawable.clear
}
}
}
// Main.kt
fun main() {
val rain = Weather.RAIN
println(rain.name) // Output: Rain
println(rain.ordinal) //Output: 2
}
從 Koltin 1.1 之後加入了 enumValue
和 enumValueOf
來列出 enum 中所有的值,這兩個功能我在開發上是沒有使用過,但他們的使用方法很簡單
Example:
val weathers = enumValues<Weather>().joinToString {
"${it.ordinal} ${it.name} : value is ${it.weather} "
}
// Output:
// 0 CLEAR : value is Clear
// 1 CLOUDY : value is Cloudy
// 2 RAIN : value is Rain
// 3 FOG : value is Fog
// 4 SNOW : value is Snow
Data classes 是 Kotlin 中裝載物件資料的類別, 當 Compiler 看到 Data classes 會自動生成
Data Class 規定
Primary Constructor 必須要有至少要存在一個 parameter,且每個 paremeter 一定要是 var 或是 val
不能繼承,只能實作 Interface
Data class 不能是 abstract, open, sealed or inner
[color=Orange]
那用 Class 和 Data Class 寫出來的類別有什麼差別? 我們可以先用兩種方法實作一樣目的 Class ,那用 toString()
將兩個被實作出來的物件印出來,可以發現
hashCode
Example:
fun main() {
val dog = Dog("")
println(dog.toString())
// Output: Dog@5451c3a8
val dogDataClass = DogDataClass()
println(dogDataClass.toString())
// Output: DogDataClass(name=, age=0, legs=4)
}
// 一般的 Class
class Dog(val name: String) {
var age: Int = 0
val legs: Int = 4
}
// Data class
data class DogDataClass (
var name: String = "",
var age: Int = 0,
var legs: Int = 4
)
因此可以發現用 Data classd 可以減少很多重複性的 Code
另外, Kotlin 有提供 Pair
和 Triple
可以使用,但官方還是推荐使用 Data class ,可讀性會比較高
我剛開始看到 Sealed 超級問號,而且一直都沒使用,後來在讀文章的時候才知道原來他是為了限制 Class 的繼承,其實廣義來說他的目的和 Enum class 相同,但是 Enum class 只能有一個 Instance ,而 Sealed class 可以有多個不同狀態的 Instance
在使用 Sealed Class 時需要注意
主要會使用 Sealed class 的場景多為搭配 when
,例如很適合拿來實作 Error Handling
像下面的例子就是一種實作 Error Handling
的方法,有幾個地方可以注意
Result
為 sealed class
,並且有兩個泛行參數 <out T, out E>
class
, 分別為 Ok
以及 Err
,並解都繼承 Result
Ok :如果沒有
Error
時,那會回傳指定的型態T
Err :如果有錯誤,那就會回傳指定的型態E
[color=Orange]
Example:
// Error Handling
sealed class Result<out T, out E>
class Ok<out T>(val value: T) : Result<T, Nothing>()
class Err<out E>(val error: E) : Result<Nothing, E>()
那在使用上可以像這樣用
Example:
fun main() {
when(val result = readFile('./some/file.txt')) {
is Ok -> println("Read file success. Size of file is $result.value.length()")
is Err -> println(result.error)
// 如果成功會 Output 檔案的大小
// 如果失敗會 Output "Get some error when reading file"
}
}
fun readFile(path: String): Result<File, Error> {
// 開始讀檔 ...
val file = File(path)
if( file.length() > 0 ) {
return Ok(File)
} else {
// 這邊只是簡單解釋一下,事實上使用 try-catch 回傳 exception message 會更好
return Err(Error("Get some error when reading file"))
}
}
Nested Classes
很容易理解,就是在 Class 中定義出另一個 Class,就是 Nested Class
, 但是 Nested Class
不能持有外部類別
Example:
class Dog(val name: String) {
var age: Int = 0
val legs: Int = 4
class dogNestedClass {
fun show() {
println(legs) // Error ,不能持有外部類別,因此不能用外部類別的成員
}
}
}
而 Inner Classes
也類似於 Nested Classes
,但是他可以持有外部類別的成員
Example:
class Dog(val name: String) {
var age: Int = 0
val legs: Int = 4
inner class dogNestedClass {
fun show() {
println(legs) // Success ,會印出 4
}
}
}
Singleton
是很長使用的一種設計模式,可以保證一個 Class 在 Memory 中只會有一個 Instance,這樣有利於協調系統整體的行為,才不會生出一堆又互相 Dependency 或互相影響,但在如何控管 Singleton
不會有搶資源或衝突上就又是另一個煩惱了
那 Kotlin 中的 Object
本身就實現了 Singleton
的效果,初始化後不會再改變,像是下面的例子,我們就幫 Bob 建立出一個只屬於他的 Object
Example:
// Bob.kt
object Bob {
val age: Int = 3
fun md5Password() = EncoderByMd5(age)
}
// Main.kt
fun main() {
val bobAge = Bob.age
val bobPassword = Bob.md5Password()
println(bobAge) // Output: 3
println(bobPassword) // Output: md5後的值
}
而 Kotln 中因為沒有像是 Java class 中有 static
的區塊,因此就要提到 Companion Object
,他等同於 static
,會存在 Class 之下
那下面的例子可以注意到
class
中,存在一個 companion object
的區塊,區塊中建立了一個 TAG
的 Stringmain()
中要取用 Dog
的 TAG
時,必須要加上 Companion
才可取得Example:
// Dog.kt
class Dog{
// 相當於 Java 中 static 的區塊
companion object {
val TAG: String = "Dog"
}
}
// Main.kt
fun main() {
println(Dog.Companion.TAG) // Output: Dog
}
那 Object 和 Companion Object 在 Kotlin 都滿常會被使用到的,這邊我非常推荐可以讀這篇文章,我個人認為寫的非常詳細,推推!!!
終於趕在 11:40 分之前把文章趕完了,今天一個腎上腺素飆生RRRRRRR!!!!!
今天也講完 OOP,也相當於講完 Kotlin 比較基礎的部份,但其實還有很多眉眉角角還沒提到,之後在開始寫 App 的時候會在慢慢補起來的
另外,這邊也要推推 Kotlin 鐵人陣 團隊也開賽囉,裡面有很多很強的大大,寫的文章都很有深度,也推荐大家去讀讀看,一定會有幫助的