在前面幾個章節中,詳細的介紹了Kotlin
類的類別中的資料類
、密封類
、介面類
以及列舉類
。在這個章節中會對Koltin
的抽象類
和內部類
作出一個詳細的講解。如果對上面所提到的類的類別還不是很清晰的,請閱讀我的前幾篇文章。
Kotlin——中級篇(六):資料類(data)、密封類詳解
Kotlin——中級篇(五):列舉類(Enum)、介面類(Interface)詳解
Kotlin——中級篇(一):類(class)詳解
目錄
一、抽象類
- 我們知道,在實際的開發程式的時候,一般都會寫一個基類,封裝常用方法、以及處理一些共有的邏輯,但是程式邏輯是根據每個類不同的功能實現不同的程式碼。而這個所謂的基類,一般都是一個抽象類。不管是
Java
還是Kotlin
,實現其抽象類的作用就在於此。那麼什麼是抽象類呢,它是怎麼定義的,它又要怎麼使用呢?
1、抽象類的定義
抽象類,可以理解為類定義了一個模板。所有的子類都是根據這個模板是填充自己的程式碼。
1.1、關鍵字
- 宣告一個抽象(類或函式)的關鍵字為:
abstract
其中值得注意的是:抽象可以分為抽象類、抽象函式、抽象屬性。而一個抽象類和普通類的區別在於抽象類除了可以有其自己的屬性、建構函式、方法等組成部分,還包含了抽象函式以及抽象屬性。
例:
abstract class Lanauage{
val TAG = this.javaClass.simpleName // 自身的屬性
// 自身的函式
fun test() : Unit{
// exp
}
abstract var name : String // 抽象屬性
abstract fun init() // 抽象方法
}
/**
* 抽象類Lanauage的實現類TestAbstarctA
*/
class TestAbstarctA : Lanauage(){
override var name: String
get() = "Kotlin"
set(value) {}
override fun init() {
println("我是$name")
}
}
/**
* 抽象類Lanauage的實現類TestAbstarctB
*/
class TestAbstarctB : Lanauage(){
override var name: String
get() = "Java"
set(value) {}
override fun init() {
println("我是$name")
}
}
fun main(args: Array<String>) {
// val lanauage = Lanauage() 是錯誤的,因為抽象類不能直接被例項化
val mTestAbstarctA = TestAbstarctA()
val mTestAbstarctB = TestAbstarctB()
println(mTestAbstarctA.name)
mTestAbstarctA.init()
println(mTestAbstarctB.name)
mTestAbstarctB.init()
}
複製程式碼
輸出結果為:
Kotlin
我是Kotlin
Java
我是Java
複製程式碼
1.2、小結
- 抽象類本身具有普通類特性,以及組成部分。不過值得注意的是,抽象類不能直接被例項化
- 其抽象了類的子類必須全部重寫帶
abstract
修飾的屬性和方法。- 抽象成員只有定義,沒有實現。都有
abstract
修飾符修飾。- 抽象類是為其子類定義了一個模板。不同是類實現不同的功能
2、抽象類的規則
- 在
Kotlin
中的抽象類在頂層定義的時候只能使用public
可見性修飾符修飾。- 抽象類中可以定義內部抽象類。
- 只能繼承一個抽象類。
- 若要實現抽象類的例項化,需要依靠子類採用向上轉型的方式處理。
- 抽象類可以繼承自一個繼承類,即抽象類可以作為子類。不過,抽象類建議不用
open
修飾符修飾,因為可以覆寫抽象類的父類的函式。
例:
open class Base{
open fun init(){}
}
abstract class Lanauage : Base(){
val TAG = this.javaClass.simpleName // 自身的屬性
// 自身的函式
fun test() : Unit{
// exp
}
abstract var name : String // 抽象屬性
abstract override fun init() // 覆寫父類的方法
abstract class Name(){} // 巢狀抽象類,可檢視第二節中的巢狀類使用
}
fun main(args: Array<String>) {
// 若要實現抽象類的例項化,需要依靠子類採用向上轉型的方式處理。
val mLanauage : Lanauage = TestAbstarctB()
}
複製程式碼
3、抽象類的實際應用
- 在
Java
的設計模式中,有一種設計模式叫模板設計模式
,其定義為:
- 定義一個操作中演算法的骨架,而將一些步驟延遲到子類中,模板方法使得子類可以不改變演算法的結構即可重定義該演算法的某些特定步驟。
- 通俗點的理解就是 :完成一件事情,有固定的數個步驟,但是每個步驟根據物件的不同,而實現細節不同;就可以在父類中定義一個完成該事情的總方法,按照完成事件需要的步驟去呼叫其每個步驟的實現方法。每個步驟的具體實現,由子類完成。
Kotlin
和Java
是互通的,說明Kotlin
也是支援這種設計模式的。
如果你對Java
中的模板設計模式不是很瞭解的,請參見這篇文章
二、內部類(巢狀類)
在實際開發中,用到內部類的地方是很多的。比如說:
- 對於
Android
開發來說,列表介面卡(adapter
)中的ViewHolder
類,就是一個內部類。- 根據後臺開發人員提供的
json
字串生成的物件中,也包含另外一個物件,這也是一個內部類。
1、巢狀類
上面提到的兩種情況,是在開發中最常見的。當然,說到內部類,就必須世道另一個概念巢狀類,所謂的巢狀類:即指一個類可以巢狀在其他類中。
例:
class Other{ // 外部類
val numOuther = 1
class Nested { // 巢狀類
fun init(){
println("執行了init方法")
}
}
}
fun main(args: Array<String>) {
Other.Nested().init() // 呼叫格式為:外部類.巢狀類().巢狀類方法/屬性
}
複製程式碼
輸出結果為:
執行了init方法
複製程式碼
注意:
- 呼叫巢狀類的屬性或方法的格式為:
外部類.巢狀類().巢狀類方法/屬性。在呼叫的時候巢狀類是需要例項化的
。- 巢狀類可以訪問外部類的屬性和成員
2、內部類
在上面的例子中講解了巢狀類的使用,而內部類和巢狀類還是有一定的區別的,而且內部類是有特定的關鍵字去宣告的。
2.1、關鍵字
宣告一個內部類使用
inner
關鍵字。 宣告格式:inner class 類名(引數){}
例:
class Other{ // 外部類
val numOther = 1
inner class InnerClass{ // 巢狀內部類
val name = "InnerClass"
fun init(){
println("我是內部類")
}
}
}
fun main(args: Array<String>) {
Other().InnerClass().init() // 呼叫格式為:外部類().內部類().內部類方法/屬性
}
複製程式碼
注意:
- 呼叫內部類的屬性或方法的格式為:
外部類().內部類().內部類方法/屬性。在呼叫的時候外部類、內部類都是需要例項化的
。- 內部類可以訪問外部類的屬性和成員
2.2、匿名內部類
作為一名
Android
開發者,對匿名內部類都不陌生,因為在開發中,匿名內部類隨處可見。比如說Button
的OnClickListener
,ListView
的單擊、長按事件等都用到了匿名內部類。 一般的使用方式為定義一個介面,在介面中定義一個方法。
例:
class Other{
lateinit private var listener : OnClickListener
fun setOnClickListener(listener: OnClickListener){
this.listener = listener
}
fun testListener(){
listener.onItemClick("我是匿名內部類的測試方法")
}
}
interface OnClickListener{
fun onItemClick(str : String)
}
fun main(args: Array<String>){
// 測試匿名內部類
val other = Other()
other.setOnClickListener(object : OnClickListener{
override fun onItemClick(str: String) {
// todo
println(str)
}
})
other.testListener()
}
複製程式碼
輸出結果為:
我是匿名內部類的測試方法
複製程式碼
遺留的問題
在上面實現的匿名內部類是很常規的用法以及寫法。在我們的實際開發當中也是大家熟知的寫法。但是在我們實際開發當中,會引入
lambda
語法糖,讓我們的專案支援lambda
語法,簡化程式碼量。在這裡我也是想把用**lambda
語法實現匿名內部類實現的程式碼貼出來**,但是考慮到很多朋友不知道lambda
語法強大或者對lambda
不瞭解,同時考慮到篇幅原因,故而在我的另一篇博文Kotlin——高階篇(一):Lambda表示式詳解去講解。
3、區域性類
所謂區域性類,這一點和
Java
是一致的。即定義在方法(函式)中的類。
例:
class Other{ // 外部類
val numOther = 1
fun partMethod(){
var name : String = "partMethod"
class Part{
var numPart : Int = 2
fun test(){
name = "test"
numPart = 5
println("我是區域性類中的方法")
}
}
val part = Part()
println("name = $name \t numPart = " + part.numPart + "\t numOther = numOther")
part.test()
println("name = $name \t numPart = " + part.numPart + "\t numOther = numOther")
}
}
fun main(args: Array<String>) {
// 測試區域性類
Other().partMethod()
}
複製程式碼
輸出結果為:
name = partMethod numPart = 2 numOther = 1
我是區域性類中的方法
name = test numPart = 5 numOther = 1
複製程式碼
通過上面的例項:我們可以看出:
- 區域性類只能在定義該區域性類的方法中使用。
- 定義在例項方法中的區域性類可以訪問外部類的所有變數和方法。但不能修改
- 區域性類中的可以定義屬性、方法。並且可以修改區域性方法中的變數。
4、靜態類
熟悉
Java
的朋友都知道Java
的靜態類,或者說用static
修飾符修飾的類。但是在Kotlin
中,是不存在static
關鍵字的。那麼我們怎樣去實現一個靜態類呢?
關於靜態類的使用,以及靜態類的語法。以及Koltin
的單例模式實現。由於篇幅原因我在這裡就不展示了。有興趣的朋友請參見kotlin中的object更像是語法糖。這篇文章是別的大牛詮釋靜態類以及單例實現很好的文章。後面我會出一篇詳細的文章為大家講解。
三、總結
在學完本篇博文中,你應該掌握抽象類
的作用,掌握其和普通類
、介面類
、繼承類
的區別所在,瞭解實現抽象類
的意義,或者說在專案中為什麼要用抽象類
去編寫一個基類
等。
對於巢狀類
和內部類
而言,知道這兩者的區別所在,和熟知他們在專案中用在什麼地方就夠了。對於靜態類
來說,常用的實現都是用其去實現一個單例模式。在Koltin
的不像Java
一樣實現很多的工具類,因為Kotlin
中的擴充套件功能很強大。可以用擴充套件去替換掉大部分的工具類。本篇文章主要是展示object
的用法而已,詳細的使用場景和用法會在後續的文章中為大家奉上。
如果各位大佬看了之後感覺還闊以,就請各位大佬隨便star
一下,您的關注是我最大的動力。
我的個人部落格:Jetictors
我的github:Jetictors