Kotlin的密封(Sealed)類:超強的枚舉(KAD 28)


作者:Antonio Leiva

時間:Jun 27, 2017

原文鏈接:https://antonioleiva.com/sealed-classes-kotlin/

 

 

Kotlin的封裝類是Java中沒有的新概念,並為此開辟了另一片可能性新的世界。

 

密封類允許你表達約束層次結構,其中對象只能是給定類型之一

 

 

也就是說,我們有一個具有特定數量的子類的類。最后,我們得到的結論是非常類似枚舉的概念。所不同的是,在枚舉中,我們每個類型只有一個對象;而在密封類中,同一個類可以擁有幾個對象。

 

 

這種差異允許密封類的對象可以保持狀態這給我們帶來一些的優勢(稍后會看到),它也為函數性概念敞開大門。

 

 

怎樣使用密封類

 

 

 

實際上,實現密封類很簡單。讓我們來看一組能夠應用於整數操作的例子。

 

 

實現情況如下:

 

1 sealed class Operation {
2     class Add(val value: Int) : Operation()
3     class Substract(val value: Int) : Operation()
4     class Multiply(val value: Int) : Operation()
5     class Divide(val value: Int) : Operation()
6 }

 

 

我們創建一個名為Operation的密封類,它包含四種操作:加法,減法,乘法和除法。

 

 

這一好處是,現在when表達式要求我們為所有可能的類型提供分支:

 

1 fun execute(x: Int, op: Operation) = when (op) {
2     is Operation.Add -> x + op.value
3     is Operation.Substract -> x - op.value
4     is Operation.Multiply -> x * op.value
5     is Operation.Divide -> x / op.value
6 }

 

 

如果你離開任何一個子類,when會抱怨其不會編譯如果你實現它們,你不需要else語句。通常,由於我們確信我們對所有人都做正確的事情,不推薦這樣做。

 

 

因為它會在編譯時失敗,並且不會運行,如果你決定添加新操作,這樣做也非常好。現添加一對操作,增量和減量:

 

1 sealed class Operation {
2     ...
3     object Increment : Operation()
4     object Decrement : Operation()
5 }

 

 

現在,你會看到編譯器警告你,存在一個問題。只需為這些新操作添加分支:

 

1 fun execute(x: Int, op: Operation) = when (op) {
2     ...
3     Operation.Increment -> x + 1
4     Operation.Decrement -> x - 1
5 }

 

 

你可能已經注意到我做了不同的事情。我使用對象而不是類。這是因為如果一個子類不保持狀態,它只能是一個對象。你為該類創建的所有實例將完全相同,它們不能有不同的狀態。

 

那么,在when表達式中,對那些情況你可以擺脫is。在這里,因為只有一個實例,你只能比較對象,你不需要檢查對象的類型。如果為那些,你也可以保留is,它也能工作。

 

 

如果你仔細考慮一下,所有子類都是對象的密封類與枚舉相同。

 

 

將副作用移到單點上

 

 

 

函數編程的副作用是一個非常通用概念。函數編程在很大程度上依賴於給定的功能,相同的參數將返回相同的結果

 

 

任何修改狀態都可能會破壞這一假設。但是任何程序都需要更改狀態,與輸入/輸出元素進行通訊等。因此,重要的是如何在我們的代碼中發現這些操作,並很容易隔離到特定地方。

 

例如,在Android視圖上實現的任何操作都被視為副作用,因為視圖的狀態修改,而函數不知道。

 

我們可以創建一個密封類,使我們能夠對視圖進行操作。基於這個概念,以前的例子:

 

 1 sealed class UiOp {
 2     object Show: UiOp()
 3     object Hide: UiOp()
 4     class TranslateX(val px: Float): UiOp()
 5     class TranslateY(val px: Float): UiOp()
 6 }
 7 
 8 fun execute(view: View, op: UiOp) = when (op) {
 9     UiOp.Show -> view.visibility = View.VISIBLE
10     UiOp.Hide -> view.visibility = View.GONE
11     is UiOp.TranslateX -> view.translationX = op.px
12     is UiOp.TranslateY -> view.translationY = op.px
13 }

 

 

記住:因為我們不需要不同的實例,沒有狀態的操作就可以是對象。

 

 

現在,你可以創建一個Ui對象,匯集要在視圖上做的所有接口操作,直到我們需要時,才執行它。

 

 

我們將描述我們想要做什么,然后我們可以創建一個執行它們的組件:

 

1 class Ui(val uiOps: List = emptyList()) {
2     operator fun plus(uiOp: UiOp) = Ui(uiOps + uiOp)
3 }

 

 

Ui類存儲操作列表,並指定一個累加和運算符,這將有助於使所有內容更清晰,更易於閱讀。現在我們可以指定要執行的操作列表:

 

1 val ui = Ui() +
2         UiOp.Show +
3         UiOp.TranslateX(20f) +
4         UiOp.TranslateY(40f) +
5         UiOp.Hide
6 
7 run(view, ui)

 

 

 

然后運行它。這里我只是使用一個run函數,但如果需要,這可以是一個完整的類。

1 fun run(view: View, ui: Ui) {
2     ui.uiOps.forEach { execute(view, it) }
3 }

 

 

想象一下這些,現在你所做的一切都是按順序運行的,但是這可能會很復雜。

 

 

run函數可以傳遞給另一個函數或類,並且那些操作的運行方式將是完全可互換的記住你可以將函數作為參數傳遞

 

 

結論

 

 

密封類的概念非常簡單,但是如果您之前沒有使用函數式編程,則需要一些使用新概念的基礎。

 

 

我必須說,由於我在函數式編程方面的知識限制,我還沒有最大限度的使用密封類。

 

 

如果您像我一樣熱衷於此,我建議你查看以前的文章,您可以在其中了解更多有關Kotlin信息,或者在本書中了解如何使用Kotlin從頭開始創建一個完整的Android應用程序

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

val ui = Ui() +

        UiOp.Show +

        UiOp.TranslateX(20f) +

        UiOp.TranslateY(40f) +

        UiOp.Hide

 

run(view, ui)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM