Nested Class (巢狀類別)和 Inner Class (內部類別) 兩者好像很類似,但其實不大一樣。
Nested Class 如下,也就是 class 包了 class,但要注意下列幾點
Outer.Nested()
class Outer {
val outer = "outer property"
class Nested {
val nested = " nested property"
fun print() {
// println(outer) // 無法存取到外部的 outer
println(nested)
}
}
}
fun main() {
val nestedd = Outer()
nestedd.outer
val nested = Outer.Nested() // 實例化內部 class
nested.print()
}
下圖就是顯示了無法存取外部 class 的屬性
如果只實例化外部 class,也存取不到內部的 class,這再次說明彼此是獨立的。
Inner Class 就跟巢狀類別不一樣了,內部 class 只要加上 inner 關鍵字,內部 class 就會成為外部 class 的一員,所以也就能存取到外部 class 的屬性,所以重點如下
val outer = Outer()
才能建立內部 class, val inner = outer.Inner()
class Outer {
val outer = "outer property"
inner class Inner {
val inner = " inner property"
fun print() {
println(outer) // 可以存取到外部的 outer
println(inner)
}
}
}
fun main() {
// inner class
val outer = Outer()
val inner = outer.Inner()
inner.print()
}
但值得注意的是 inner class 會有造成 memory leak 的風險,請小心使用!在以往 Java 的經驗來說,其實不大會使用 inner class。
有興趣可以參考此篇:Understanding Memory Leaks in Java(https://www.baeldung.com/java-memory-leaks)
object 是 Kotlin 很方便的一個關鍵字,他會直接幫你創造實體化一個 thread safe 的 Singleton Object
下面的例子,我舉了一個像是序號產生器的例子,這裡不使用 class 宣告,直接使用 object 宣告這是一個 Singleton 物件。
object SerialNoGenerator {
var count = 0;
fun gen(): Int {
count++
println(count)
return count
}
}
在 main 中就可以直接用 class name 來呼叫方法 SerialNoGenerator.gen()
,不需要自己建立物件。
fun main() {
SerialNoGenerator.gen()
SerialNoGenerator.gen()
SerialNoGenerator.gen()
SerialNoGenerator.gen()
}
結果:
1
2
3
4
也由於會自動幫我們建立物件,所以沒有 constructor(),如果要做初始化或檢查的事,可以使用 init block
object SerialNoGenerator {
var count = 0
init {
// do init...
}
fun gen(): Int {
count++
println(count)
return count
}
}
object 的類別可以繼承其他 class,如下,如此 SerialNoGenerator
就可以使用父類別的 printGeneratorNo()
open class Generator {
fun printGeneratorNo(no: Int) {
println(no)
}
}
object SerialNoGenerator : Generator() {
var count = 0
init {
// do init...
}
fun gen(): Int {
count++
printGeneratorNo(count)
return count
}
}
因為不需要自己創立物件,直接這樣呼叫就好 SerialNoGenerator.gen()
使得看起來很像靜態呼叫方法 (static invoke),但其實 Kotlin 並沒有 static 的關鍵字存在,當然去查看 byte code,底層實作 singleton object 也是 static 的,但 Kotlin 在這 object 的重點是他幫你創造了一個實體,記得這個就好了。
object expression 就像是匿名類別的實作
如下例
多宣告了一個 BaseClass class,都是 open 代表可以繼承和覆寫方法。
open class BaseClass {
open fun getParameter(): String {
return "parameters"
}
}
open class Generator {
fun printGeneratorNo(no: Int) {
val parameter = object : BaseClass() {
override fun getParameter() = "Generator's parameter"
}
println("parameter: ${parameter.getParameter()}")
println("no: $no")
}
}
在 Generator 內, 可以直接使用 object expression 直接實做和實例化出這個物件,然後使用。
val parameter = object : BaseClass() {
override fun getParameter() = "Generator's parameter"
}
最後就可以直接呼叫
println("parameter: ${parameter.getParameter()}")
Companion Object 主要有兩個作用
在 SerialNoGeneratorK 內宣告 companion object {} 其中的 count 和 gen() 就會有類似靜態屬性和方法的效果。
class SerialNoGeneratorK {
companion object {
var count = 0
fun gen(): Int {
count++
println(count)
return count
}
}
}
fun main() {
SerialNoGeneratorK.gen()
SerialNoGeneratorK.gen()
SerialNoGeneratorK.gen()
SerialNoGeneratorK.gen()
如之前 Day 11 有提到的,private constructor 就是用在這時候
這裡使用 companion object 達到像工廠模式的方式,只透過 create() 來建立物件,而不能透過 class 的 constructor(被宣告成 private 了)
class Entity private constructor(val someData: String) {
companion object Factory{
fun create(): Entity {
return Entity("someData")
}
}
}
最後創造物件時可以有 companion object 的名稱 Entity.Factory.create()
,也可以不用名稱 Entity.create()
fun main() {
val entity = Entity.Factory.create()
println(entity)
val entity2 = Entity.create()
println(entity2)
}
以上就是今天的內容!我們明天見!
今日練習的程式在這: 請點我