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