DslAdapter开发简介DslAdapter是一个Android RecyclerView的Adapter构建器, DSL语法, 面向组合子设计. 专注类型安全, 所有代码采用Kotlin编写.为什么要开发DslAdapter实际上在DslAdapter开始开发的时点已经有很多RecyclerAdapter的扩展库存在了, 有的甚至从2015年开始已经持续开发到了现在. 从功能上来说, 这些库的各项功能都非常成熟了, 几乎所有的需求都有涉及; 而从思想上来说, 各种构建方式都有相应的库 以现在很常见的库举例:CymChad/BaseRecyclerViewAdapterHelper 1w6 star这是Github上检索出Star最多的RecyclerAdapter的库, 它支持添加Item事件、添加列表加载动画、添加头部、尾部、树形列表等等,甚至设置空布局FastAdapter 这个库是以前项目中也使用过的库, 功能也相当丰富, 相比上面的库更注重List的功能, 是一个从2015年开始开发一直持续维护的库KidAdapter kotlin编写,DSL语法的构建器,利用了kotlin的语法特性,构建上更简单,同时也实现了types功能。但功能上相比上面的库就简单很多了甚至我多年前也已经写过一个相关库AdapterRenderer,也实现了很多功能。可以说RecyclerAdapter领域是最难以有新突破的地方了,似乎能做的只是在现有基础上小修小改而已了。但现有的库真的已经完美到这种程度了吗?不,现有的库也有很多的缺点:非强类型,为了兼容多类型而直接忽略数据类型信息,失去了编译期类型检查,只能靠编写时自我约束繁琐的模板代码。有些库会需要继承基础Adapter或者基础Holder继承方法来实现功能,因此会写大量的XXAdapter、XXItem逻辑代码被迫分离。也是由于需要单独写XXAdapter、XXItem,而这些小类抽象度低,该界面的逻辑被迫分离到了这些小类中,即使是业务联系很大的逻辑功能过于繁杂,抽象度低。功能上虽然丰富,但实际上包括了很多并不是RecyclerView应该关注的功能,导致变成了一个全功能的Adapter,Adapter的逻辑结构极其复杂,难以维护也难以扩展类中变量和算法混杂,需要时常注意状态的同步问题正因如此,为了解决以上这些问题,让我们编写的Adapter更简单、更安全,从而有了开发一个新库的想法一个新的Adapter库应该是什么样的想要构建一个Adapter的库,首先我们要想想我们这个新库应该是干什么的,这就需要回到RecyclerView这个库中Adapter被定义为什么。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}Adapter设计根据前一章的描述,我们构建的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被我们定义为基础组合子,那我们需要哪些Renderer呢:EmptyRenderer: 空Renderer, count为0LayoutRenderer: 与View绑定的末端Renderer, 可自定义数量ConstantItemRenderer: 将常量绑定到View的末端Renderer, 可适配任意数据源, 可自定义数量MapperRenderer: 转换目标Renderer的数据源类型, 一般通过mapT()来使用它ListRenderer: 将目标Renderer转换为适配列表数据源SealedItemRenderer: 根据数据源具体数据选择不同的Renderer渲染, 比如对于Int?类型,可以在为null的时候用EmptyRenderer渲染; 不为null的时候使用LayoutRenderer渲染ComposeRenderer: 组合多个不同RendererDataBindingRenderer : Android Databinding支持的Renderer复杂的结构基本都可以通过组合他们来实现: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 HListobject 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))同样是构建列表, 异构列表包含了更丰富的类型信息:容器的size为2容器中第一个元素为String, 第二个为Int相比传统列表,异构列表的优势:完整保存所有元素的类型信息自带容器的size信息完整保存每个元素的位置信息这是基本的异构列表,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
...