DslAdapter是一個Android RecyclerView的Adapter構建器, DSL語法, 面向組合子設計. 專一類型安全, 全部代碼採用Kotlin編寫.git
實際上在DslAdapter開始開發的時點已經有不少RecyclerAdapter的擴展庫存在了, 有的甚至從2015年開始已經持續開發到了如今. 從功能上來講, 這些庫的各項功能都很是成熟了, 幾乎全部的需求都有涉及; 而從思想上來講, 各類構建方式都有相應的庫 github
以如今很常見的庫舉例:算法
添加Item事件
、添加列表加載動畫
、添加頭部、尾部
、樹形列表
等等,甚至設置空佈局
甚至我多年前也已經寫過一個相關庫AdapterRenderer,也實現了不少功能。能夠說RecyclerAdapter領域是最難以有新突破的地方了,彷佛能作的只是在現有基礎上小修小改而已了。編程
但現有的庫真的已經完美到這種程度了嗎?安全
不,現有的庫也有不少的缺點:數據結構
全功能的Adapter
,Adapter的邏輯結構極其複雜,難以維護也難以擴展正因如此,爲了解決以上這些問題,讓咱們編寫的Adapter更簡單、更安全,從而有了開發一個新庫的想法app
想要構建一個Adapter的庫,首先咱們要想一想咱們這個新庫應該是幹什麼的,這就須要回到RecyclerView這個庫中Adapter被定義爲何。ide
RecyclerAdapter被定義爲數據適配器
,即將數據
和視圖
進行綁定:函數
數據 --映射--> 視圖List
而RecyclerView之因此很強大是由於它已經不只僅是用於顯示List,它會須要顯示覆雜的視圖結構,好比樹狀圖、可展開列表等等佈局
數據 --映射--> 複雜結構 --渲染--> 視圖List
咱們的Adapter庫須要完成的工做簡單來講就是:將數據變換爲複雜的抽象結構(好比樹狀),再將複雜的抽象結構渲染爲視圖List(由於RecyclerView最終只支持平整單列表)
實際咱們須要實現的是一個變換問題,不管最終咱們須要的抽象結構是簡單的List仍是複雜的樹狀圖本質上都只是作這麼一個數據的變換,所以這個變換函數
就是咱們的基礎組合子
,咱們能夠經過基礎組合子的相互組合實現複雜功能
這個基本組合子就是DslAdapter庫中的BaseRenderer
類:
interface Renderer<Data, VD : ViewData<Data>> { fun getData(content: Data): VD fun getItemId(data: VD, index: Int): Long = RecyclerView.NO_ID fun getItemViewType(data: VD, position: Int): Int fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder fun bind(data: VD, index: Int, holder: RecyclerView.ViewHolder) fun recycle(holder: RecyclerView.ViewHolder) }
它包含做爲RecyclerAdapter基礎組合子須要的幾個基本方法。
函數範式中反作用
是要嚴格分離的,而變量
就是一種反作用,若是容許變量在Renderer中不受管制的存在會使Renderer組合子自己失去可組合性,同時數據也變得很不可靠(線程不安全、Renderer並不保證只在一個地方使用一次)
而能夠看到Renderer的基礎方法中定義的都是純函數,而且不包含容許反作用存在的IO
等類型(這裏的IO
不一樣於Java中的input/output,而是指Haskell中的IO類型類),所以數據被設計爲與Renderer嚴格分離(數據與算法的嚴格分離),ViewData便是這個被分離的數據
interface ViewData<out OriD> : ViewDataOf<OriD> { val count: Int val originData: OriD }
根據前一章的描述,咱們構建的Renderer就是一個映射函數,所以Adapter也只須要使用這個映射函數將數據進行渲染便可
class RendererAdapter<T, VD : ViewData<T>>( val initData: T, val renderer: BaseRenderer<T, VD> ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val dataLock = Any() private var adapterViewData: VD = renderer.getData(initData) ... override fun getItemCount(): Int = adapterViewData.count override fun getItemViewType(position: Int): Int = renderer.getItemViewType(adapterViewData, position) override fun getItemId(position: Int): Long = renderer.getItemId(adapterViewData, position) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = renderer.onCreateViewHolder(parent, viewType) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) = renderer.bind(adapterViewData, position, holder) override fun onFailedToRecycleView(holder: RecyclerView.ViewHolder): Boolean { renderer.recycle(holder) return super.onFailedToRecycleView(holder) } override fun onViewRecycled(holder: RecyclerView.ViewHolder) { renderer.recycle(holder) } }
能夠看到Adapter實際只是將Renderer中的各個函數實際應用於原生RecyclerView.Adapter
的各個方法中便可,極其簡單
另外,ViewData
只保存於Adapter中一份,它其實就包含整個Adapter的全部狀態,換句話說只要保存它,能夠徹底恢復RecyclerView的數據狀態;另外ViewData
是被鎖保護起來的,保證數據的線程安全性
Renderer被咱們定義爲基礎組合子,那咱們須要哪些Renderer呢:
mapT()
來使用它Int?
類型,能夠在爲null
的時候用EmptyRenderer渲染; 不爲null
的時候使用LayoutRenderer渲染複雜的結構基本均可以經過組合他們來實現:
val adapter = RendererAdapter.multipleBuild() .add(layout<Unit>(R.layout.list_header)) .add(none<List<Option<ItemModel>>>(), optionRenderer( noneItemRenderer = LayoutRenderer.dataBindingItem<Unit, ItemLayoutBinding>( count = 5, layout = R.layout.item_layout, bindBinding = { ItemLayoutBinding.bind(it) }, binder = { bind, item, _ -> bind.content = "this is empty item" }, recycleFun = { it.model = null; it.content = null; it.click = null }), itemRenderer = LayoutRenderer.dataBindingItem<Option<ItemModel>, ItemLayoutBinding>( count = 5, layout = R.layout.item_layout, bindBinding = { ItemLayoutBinding.bind(it) }, binder = { bind, item, _ -> bind.content = "this is some item" }, recycleFun = { it.model = null; it.content = null; it.click = null }) .forList() )) .build()
以上Adapter可圖示爲:
|--LayoutRenderer header | |--SealedItemRenderer | |--none -> LayoutRenderer placeholder count 5 | | | |--some -> ListRenderer | |--DataBindingRenderer 1 | |--DataBindingRenderer 2 | |--...
上面說到Renderer被定義爲:Renderer<T, VD : ViewData<T>>
, 其中ViewData由於和Renderer是強綁定的,因此每每一種ViewData和一種Renderer是一一對應的,在類型上ViewData和Renderer是相等的
用法上來講能夠這樣來看:類型Renderer<T, VD : LayoutViewData<T>>
能夠被等價看待爲LayoutRenderer<T>
, 相同的道理, 因爲Updater和ViewData也是一一對應的關係, 所以類型Updater<T, VD : LayoutViewData<T>>
能夠被等價看待爲LayoutUpdater<T>
所以類型LayoutViewData<T>
能夠看作LayoutXX系列全部類的一個特徵類型
:經過這個類型能夠惟一地識別出其對應的原始類型
在高階類型的Java類型系統實現中也使用了相似的手法:
能夠注意到VIewData的原始定義中繼承了ViewDataOf
類型,這個類型原始定義是這樣的:
class ForViewData private constructor() { companion object } typealias ViewDataOf<T> = Kind<ForViewData, T> @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") inline fun <T> ViewDataOf<T>.fix(): ViewData<T> = this as ViewData<T>
其中ForViewData
就是咱們定義的特徵類型
,在fix()
函數中咱們經過識別Kind<ForViewData, T>
中的ForViewData部分便可識別爲其惟一對應的ViewData<T>,從而安全地進行類型轉換
注意,特徵類型
只是用於類型系統識別用,代碼中咱們實際並不會使用它的實例,所以能夠看到上面定義的ForViewData
類型是私有構造函數,沒法被實例化
另外,實際只要是具備惟一性
任意類型均可以做爲特徵類型
,能夠利用已有的類型(好比LayoutViewData
)、也能夠單獨定義(好比ForViewData
)
利用特徵類型
咱們能夠更靈活使用類型(見Kotlin與高階類型
),也能夠簡化冗餘的類型信息
好比以前版本的DslAdapter中,Adapter的泛型信息爲:<T, VD : ViewData<T>, BR : BaseRenderer<T, VD>>
包含了T
、VD
和BR
三個類型信息,但根據咱們上面的分析,VD
和BR
兩個類型實際是等價的,他們包含了相同的類型特徵,所以咱們能夠將其簡化爲<T, VD : ViewData<T>
,兩個泛型便可保留全部咱們須要的類型信息
對於複雜的Adapter這種簡化對於減小編譯系統壓力來講的是更加明顯的:
好比原來的類型有6000多字符:
ComposeRenderer<HConsK<ForIdT, Pair<ED, SearchConditions>, HConsK<ForIdT, Pair<ED, SearchConditions>, HNilK<ForIdT>>>, HConsK<ForComposeItem, Pair<Pair<ED, SearchConditions>, SealedItemRenderer<Pair<ED, SearchConditions>, HConsK<Kind<ForSealedItem, Pair<ED, SearchConditions>>, Pair<List<Pair<ED, CategoryInfo>>, ListRenderer<List<Pair<ED, CategoryInfo>>, Pair<ED, CategoryInfo>, MapperViewData<Pair<ED, CategoryInfo>, HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, ComposeViewData<HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, HConsK<ForComposeItemData, Pair<List<Pair<ED, NormalCategoryInfo>>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>, HConsK<ForComposeItemData, Pair<CategoryInfo, SealedItemRenderer<CategoryInfo, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<Unit, EmptyRenderer<Unit>>, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<CategoryInfo, DataBindingRenderer<CategoryInfo, CategoryInfo>>, HNilK<Kind<ForSealedItem, CategoryInfo>>>>>>, HNilK<ForComposeItemData>>>>>, MapperRenderer<Pair<ED, CategoryInfo>, HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, ComposeViewData<HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, HConsK<ForComposeItemData, Pair<List<Pair<ED, NormalCategoryInfo>>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>, HConsK<ForComposeItemData, Pair<CategoryInfo, SealedItemRenderer<CategoryInfo, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<Unit, EmptyRenderer<Unit>>, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<CategoryInfo, DataBindingRenderer<CategoryInfo, CategoryInfo>>, HNilK<Kind<ForSealedItem, CategoryInfo>>>>>>, HNilK<ForComposeItemData>>>>, ComposeRenderer<HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, HConsK<ForComposeItem, Pair<List<Pair<ED, NormalCategoryInfo>>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>, HConsK<ForComposeItem, Pair<CategoryInfo, SealedItemRenderer<CategoryInfo, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<Unit, EmptyRenderer<Unit>>, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<CategoryInfo, DataBindingRenderer<CategoryInfo, CategoryInfo>>, HNilK<Kind<ForSealedItem, CategoryInfo>>>>>>, HNilK<ForComposeItem>>>, HConsK<ForComposeItemData, Pair<List<Pair<ED, NormalCategoryInfo>>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>, HConsK<ForComposeItemData, Pair<CategoryInfo, SealedItemRenderer<CategoryInfo, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<Unit, EmptyRenderer<Unit>>, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<CategoryInfo, DataBindingRenderer<CategoryInfo, CategoryInfo>>, HNilK<Kind<ForSealedItem, CategoryInfo>>>>>>, HNilK<ForComposeItemData>>>>>>>, HConsK<Kind<ForSealedItem, Pair<ED, SearchConditions>>, Pair<List<Pair<ED, NormalCategoryInfo>>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>, HNilK<Kind<ForSealedItem, Pair<ED, SearchConditions>>>>>>>, HConsK<ForComposeItem, Pair<Pair<ED, SearchConditions>, MapperRenderer<Pair<ED, SearchConditions>, List<Pair<ED, NormalCategoryInfo>>, ListViewData<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>>, HNilK<ForComposeItem>>>, HConsK<ForComposeItemData, Pair<Pair<ED, SearchConditions>, SealedItemRenderer<Pair<ED, SearchConditions>, HConsK<Kind<ForSealedItem, Pair<ED, SearchConditions>>, Pair<List<Pair<ED, CategoryInfo>>, ListRenderer<List<Pair<ED, CategoryInfo>>, Pair<ED, CategoryInfo>, MapperViewData<Pair<ED, CategoryInfo>, HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, ComposeViewData<HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, HConsK<ForComposeItemData, Pair<List<Pair<ED, NormalCategoryInfo>>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>, HConsK<ForComposeItemData, Pair<CategoryInfo, SealedItemRenderer<CategoryInfo, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<Unit, EmptyRenderer<Unit>>, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<CategoryInfo, DataBindingRenderer<CategoryInfo, CategoryInfo>>, HNilK<Kind<ForSealedItem, CategoryInfo>>>>>>, HNilK<ForComposeItemData>>>>>, MapperRenderer<Pair<ED, CategoryInfo>, HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, ComposeViewData<HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, HConsK<ForComposeItemData, Pair<List<Pair<ED, NormalCategoryInfo>>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>, HConsK<ForComposeItemData, Pair<CategoryInfo, SealedItemRenderer<CategoryInfo, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<Unit, EmptyRenderer<Unit>>, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<CategoryInfo, DataBindingRenderer<CategoryInfo, CategoryInfo>>, HNilK<Kind<ForSealedItem, CategoryInfo>>>>>>, HNilK<ForComposeItemData>>>>, ComposeRenderer<HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, HConsK<ForComposeItem, Pair<List<Pair<ED, NormalCategoryInfo>>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>, HConsK<ForComposeItem, Pair<CategoryInfo, SealedItemRenderer<CategoryInfo, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<Unit, EmptyRenderer<Unit>>, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<CategoryInfo, DataBindingRenderer<CategoryInfo, CategoryInfo>>, HNilK<Kind<ForSealedItem, CategoryInfo>>>>>>, HNilK<ForComposeItem>>>, HConsK<ForComposeItemData, Pair<List<Pair<ED, NormalCategoryInfo>>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>, HConsK<ForComposeItemData, Pair<CategoryInfo, SealedItemRenderer<CategoryInfo, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<Unit, EmptyRenderer<Unit>>, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<CategoryInfo, DataBindingRenderer<CategoryInfo, CategoryInfo>>, HNilK<Kind<ForSealedItem, CategoryInfo>>>>>>, HNilK<ForComposeItemData>>>>>>>, HConsK<Kind<ForSealedItem, Pair<ED, SearchConditions>>, Pair<List<Pair<ED, NormalCategoryInfo>>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>, HNilK<Kind<ForSealedItem, Pair<ED, SearchConditions>>>>>>>, HConsK<ForComposeItemData, Pair<Pair<ED, SearchConditions>, MapperRenderer<Pair<ED, SearchConditions>, List<Pair<ED, NormalCategoryInfo>>, ListViewData<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD>, ListRenderer<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD, BR>>>, HNilK<ForComposeItemData>>>>
簡化後只有1900多個字符:
ComposeRenderer<HConsK<ForIdT, Pair<ED, SearchConditions>, HConsK<ForIdT, Pair<ED, SearchConditions>, HNilK<ForIdT>>>, HConsK<ForComposeItemData, Pair<Pair<ED, SearchConditions>, SealedViewData<Pair<ED, SearchConditions>, HConsK<Kind<ForSealedItem, Pair<ED, SearchConditions>>, Pair<List<Pair<ED, CategoryInfo>>, ListViewData<List<Pair<ED, CategoryInfo>>, Pair<ED, CategoryInfo>, MapperViewData<Pair<ED, CategoryInfo>, HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, ComposeViewData<HConsK<ForIdT, List<Pair<ED, NormalCategoryInfo>>, HConsK<ForIdT, CategoryInfo, HNilK<ForIdT>>>, HConsK<ForComposeItemData, Pair<List<Pair<ED, NormalCategoryInfo>>, ListViewData<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD>>, HConsK<ForComposeItemData, Pair<CategoryInfo, SealedViewData<CategoryInfo, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<Unit, EmptyViewData<Unit>>, HConsK<Kind<ForSealedItem, CategoryInfo>, Pair<CategoryInfo, DataBindingViewData<CategoryInfo, CategoryInfo>>, HNilK<Kind<ForSealedItem, CategoryInfo>>>>>>, HNilK<ForComposeItemData>>>>>>>, HConsK<Kind<ForSealedItem, Pair<ED, SearchConditions>>, Pair<List<Pair<ED, NormalCategoryInfo>>, ListViewData<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD>>, HNilK<Kind<ForSealedItem, Pair<ED, SearchConditions>>>>>>>, HConsK<ForComposeItemData, Pair<Pair<ED, SearchConditions>, MapperViewData<Pair<ED, SearchConditions>, List<Pair<ED, NormalCategoryInfo>>, ListViewData<List<Pair<ED, NormalCategoryInfo>>, Pair<ED, NormalCategoryInfo>, VD>>>, HNilK<ForComposeItemData>>>>
函數範式中有List類型,但與OOP中的列表的結構是徹底不一樣的:
List a = Nil | Cons a (List a)
或者用kotlin來描述爲:
sealed class List<A> object Nil : List<Nothing> data class Cons<T>(val head: T, val tail: List<T>) : List<T>
它能夠看作是一個自包含的數據結構:若是沒有數據就是Nil;若是有數據則包含一個數據以及下一個List<T>
,直到取到Nil結束
即遞歸
數據結構
強類型化最困難的在於ComposeRenderer的強類型化,ComposeRenderer能夠實現的是組合不一樣的Renderer:
|-- item1 (eg: itemRenderer) |-- item2 (eg: stringRenderer)
val composeRenderer = ComposeRenderer.startBuild .add(itemRenderer) .add(stringRenderer) .build()
原始的實現方法能夠經過一個List來保存:
List<BaseRenderer>
但這樣就丟失了每一個元素的類型特徵信息,好比咱們沒法知道第二個元素是stringRenderer仍是itemRenderer,這也是現有全部庫都存在的問題,經常咱們只能使用強制類型轉換的方式來獲得咱們但願的類型,但沒有類型系統的檢查這種轉換是不安全的,只能在運行期被檢查出來
不管是OOP的列表仍是上面介紹的函數式列表都沒法知足這個需求,他們在add()
的時候都把類型特徵丟棄了
而異構列表
能夠將這些保留下來:
sealed class HList object HNil : HList() data class HCons<out H, out T : HList>(val head: H, val tail: T) : HList()
能夠看到它的數據結構和原始的函數式列表的結構很類似,都是遞歸數據結構
但它在泛型中增長了out T : HList
,這是一個引用了本身類型的泛型聲明,即:
HList = HCons<T1, HList> HList = HCons<T1, HCons<T2, HList>> HList = HCons<T1, HCons<T2, HCons<T3, HList>>> HList = HCons<T1, HCons<T2, HCons<T3, HCons<T4, HList>>>> ...
它的類型能夠在不斷的代換中造成類型列表,這種便是遞歸類型
使用上:
// 原函數 fun test2(s: String, i: Int): List<Any?> = listOf(s, i) // 異構列表 fun test2(s: String, i: Int): HCons<Int, HCons<String, HNil>> = HCons(i, HCons(s, HNil))
一樣是構建列表, 異構列表包含了更豐富的類型信息:
String
, 第二個爲Int
相比傳統列表,異構列表的優點:
這是基本的異構列表,DslAdapter爲了作類型限定而自定義了高階異構列表
,能夠參考源碼HListK.kt
遞歸類型是指的包含有本身的類型聲明:
fun <DL : HListK<ForIdT, DL>> test() = ...
這種泛型能夠在不斷代換中造成上面描述的類型列表:
HList = HCons<T1, HList> HList = HCons<T1, HCons<T2, HList>> HList = HCons<T1, HCons<T2, HCons<T3, HList>>> HList = HCons<T1, HCons<T2, HCons<T3, HCons<T4, HList>>>> ...
在描述數量不肯定的類型時頗有用
在使用DslAdapter中可能會注意到有時會有一個奇特的參數type
:
fun <T, D, VD : ViewData<D>> BaseRenderer<D, VD>.mapT(type: TypeCheck<T>, mapper: (T) -> D, demapper: (oldData: T, newMapData: D) -> T) : MapperRenderer<T, D, VD> = ...
這個參數的實際值並不會在函數中被使用到,而跳轉到TypeCheck
的定義中:
class TypeCheck<T> private val typeFake = TypeCheck<Nothing>() @Suppress("UNCHECKED_CAST") fun <T> type(): TypeCheck<T> = typeFake as TypeCheck<T>
TypeCheck
只是一個沒有任何功能的空類型,返回的值也是固定的同一個值,那這個參數到底是用來幹什麼的?
要解釋這個問題咱們須要看一下mapT
這個函數的調用:
LayoutRenderer<String>(MOCK_LAYOUT_RES, 3) .mapT(type = type<TestModel>(), mapper = { it.msg }, demapper = { oldData, newMapData -> oldData.copy(msg = newMapData) })
上面這段代碼的做用是將接收String
數據類型的Renderer轉換爲接受TestModel
數據類型
若是咱們不使用type
這個參數的話這段代碼會變成什麼樣呢:
LayoutRenderer<String>(MOCK_LAYOUT_RES, 3) .map<TestModel, String, LayoutViewData<String>>( mapper = { it.msg }, demapper = { oldData, newMapData -> oldData.copy(msg = newMapData) })
能夠看到因爲函數的第一個泛型T
類型系統也沒法推測出來爲TestModel
,所以須要咱們顯式地把T
的類型給寫出來,但kotlin和Java的語法中沒法只寫泛型聲明中的一項,因此咱們不得不把其餘能夠被推測出來的泛型也顯式的聲明出來,不只代碼繁雜並且在類型複雜的時候幾乎是不可完成的工做
參數type
中的泛型<T>
就能夠用於輔助編譯器進行類型推測,咱們只須要手動聲明編譯器難以自動推測的部分泛型,而其餘能夠被推測的泛型仍是交由編譯器自動完成,便可簡化代碼
而這裏的type = type<TestModel>()
便是輔助類型
,它的實例自己沒有任何做用,它只是爲了輔助編譯器進行類型檢查
DslAdapter致力於完整的靜態類型,使用了各類類型編程的技法,這是由於足夠的類型信息不只能幫編譯器進行類型檢查、早期排除問題,並且可以幫助咱們在編碼的時候編寫足夠準確
的代碼
準確
的代碼意味着咱們即不須要處理不會發生的額外狀況、也不會少處理了可能的狀況。(早期版本的DslAdapter的更新模塊就是被設計爲自動
檢查更新,致使須要處理大量額外狀況,極其不安全)
同時DslAdapter自己不是一個但願作到全能的庫,它的目標是將Adapter的工做足夠簡化,並只專一於Adapter工做,其餘功能就交給專一其餘功能的庫
Do One Thing and Do It Well.
但這並不意味着DslAdapter是一個拋棄了功能性的庫,相反,它是一個極其靈活的庫。它的核心被設計得很是簡單,只有BaseRenderer
和RendererAdapter
, 這兩個類也至關簡單,而且因爲全局只有一個變量被保存在RendererAdapter中,保證了數據的線程安全性。而數據中都是不可變屬性,使內部數據也能夠被徹底暴露出來,方便了功能的擴展
實際上DSL更新器 dsladapter-updater模塊
以及DSL position獲取器 dsladapter-position模塊
都是以擴展方式存在的,後續還會根據需求繼續擴展其餘模塊
本文只是淺嘗則止地介紹了一點DslAdapter的開發技巧,歡迎Star和提出issue