做者:Antonio Leivaandroid
時間:Jun 27, 2017編程
原文連接:https://antonioleiva.com/sealed-classes-kotlin/ide
Kotlin的封裝類是Java中沒有的新概念,併爲此開闢了另外一片可能性新的世界。函數式編程
密封類容許你表達約束層次結構,其中對象只能是給定類型之一。函數
也就是說,咱們有一個具備特定數量的子類的類。最後,咱們獲得的結論是很是相似枚舉的概念。所不一樣的是,在枚舉中,咱們每一個類型只有一個對象;而在密封類中,同一個類能夠擁有幾個對象。ui
這種差別容許密封類的對象能夠保持狀態。這給咱們帶來一些的優點(稍後會看到),它也爲函數性概念敞開大門。spa
實際上,實現密封類很簡單。讓咱們來看一組可以應用於整數操做的例子。code
實現狀況以下:orm
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)