专栏名称: 一个普普通通简简单单平平凡凡的神
目录
相关文章推荐
青岛日报  ·  知名巨头宣布解散!曾风靡全球 ·  3 天前  
青岛日报  ·  知名巨头宣布解散!曾风靡全球 ·  3 天前  
51好读  ›  专栏  ›  一个普普通通简简单单平平凡凡的神

Kotlin实战之Fuel的高阶函数

一个普普通通简简单单平平凡凡的神  · 掘金  ·  · 2018-04-18 02:55

正文

Fuel 是一个用 Kotlin 写的网络库,与 OkHttp 相比较,它的代码结构比较简单,但是它的巧妙之处在于充分利用了 Kotlin 的语言特性,所以代码看上去干净利落。

OkHttp 使用了一个 interceptor chain 来实现拦截器的串联调用,由于 Java 语言( JDK ≤ 7)本身的局限性,所以实现代码比较臃肿,可读性也不友好。当然,RxJava 再加上 retrolambda 这种 backport 的出现,一定程度上了缓解了这种尴尬,但是 Kotlin 天生具备的声明式写法又使得 Java 逊色了很多。

我们知道,拦截器本质上是一个责任链模式(chain of responsibility)的实现,我们通过具体代码来学习一下 Kotlin 究竟是如何利用高阶函数实现了拦截器功能。

首先定义一个 MutableList 用于存储拦截器实例:

  1. val requestInterceptors:

  2.  MutableList<((Request) -> Request) -> ((Request) -> Request)>

  3.   = mutableListOf()

注意,Kotlin 的类型系统明确区分了 mutable 和 immutable,默认的 List 类型是 immutable。

requestInterceptors 的元素类型是一个高阶函数:

  1. ((Request) -> Request) -> ((Request) -> Request)

作为元素类型的高阶函数,其参数也是一个高阶函数 (Request)->Request , 同时,返回值也是高阶函数 (Request)->Request

然后,我们给 requestInterceptors 定义一个增加元素的方法:

  1. fun addRequestInterceptor(

  2.  interceptor: ((Request) -> Request) -> ((Request) -> Request)) {

  3.    requestInterceptors += interceptor

  4. }

addRequestInterceptor 的参数类型

  1. (Request) -> Request) -> ((Request) -> Request)

requestInterceptors 的元素类型一致。

注意,这里又出现了一个 Kotlin 有而 Java 没有的语言特性:操作符重载。

我们没有调用 requestInterceptors.add(interceptor) ,而是用了一个 plusAssign 的操作符 += (MutableCollections.kt 中定义的操作符重载):

  1. /**

  2. * Adds the specified [element] to this mutable collection.

  3. */

  4. @kotlin.internal.InlineOnly

  5. public inline operator fun <T> MutableCollection<in T>.plusAssign(element: T) {

  6.    this.add(element)

  7. }

那么,此时应该定义一个拦截器的函数实例了:

  1. fun <T> loggingRequestInterceptor() =

  2.        { next: (T) -> T ->

  3.            { t: T ->

  4.                println(t.toString())

  5.                next(t)

  6.            }

  7.        }

loggingRequestInterceptor 是一个函数,它的返回值是一个 lambda 表达式(即高阶函数):

  1. { next: (T) -> T ->

  2.    { t: T ->

  3.        println(t.toString())

  4.        next(t)

  5.    }

  6. }

1) 这个 lambda 的 参数 next:(T)->T (参数名是 next ,参数类型是 (T)->T ), 返回值 是另一个 lambda 表达式:

  1. { t: T ->

  2.    println(t.toString())

  3.    next(t)

  4. }

2) 因为 lambda 本身是一个函数字面量(function literal),它的类型通过函数本身可以推到得出,如果我们用一个变量来引用这个 lambda 的话,变量的类型是 (T)->T

由1、2两点可知, loggingRequestInterceptor() 的返回值是一个 lambda 表达式,它的参数是 (T)->T ,返回值也是 (T)->T

这里的泛型函数略抽象,我们来看一个具体化的函数:

  1. fun cUrlLoggingRequestInterceptor() =

  2.        { next: (Request) -> Request ->

  3.            { r: Request ->

  4.                println(r.cUrlString())

  5.                next(r)

  6.            }

  7.        }

同理, cUrlLoggingRequestInterceptor() 函数的参数为 (Request)->Request 、返回值为 (Request)->Request

拦截器都定义好了,那么应该如何调用呢?Kotlin 一行代码搞定🤟::

  1. requestInterceptors.foldRight({ r: Request -> r }) { f, acc -> f(acc) }

foldRight List 的一个扩展函数,先来看声明:

  1. /**

  2. * Accumulates value starting with [initial] value and applying [operation] from right to left to each element and current accumulator value.

  3. */

  4. public inline fun <T, R> List<T>.foldRight(initial: R, operation: (T, acc: R) -> R): R {

  5.    var accumulator = initial

  6.    if (!isEmpty()) {

  7.        val iterator = listIterator(size) // 让迭代器指向最后一个元素的末尾

  8.        while (iterator.hasPrevious()) {

  9.            accumulator = operation(iterator.previous(), accumulator)

  10.        }

  11.    }

  12.    return accumulator

  13. }

函数功能总结为一句话:从右往左,对列表中的每一个元素执行 operation 操作,每个操作的结果是下一次操作的入参,第一次 operation 的初始值是 initial

回头来看拦截器列表 requestInterceptors 如何执行了 foldRight

                                                    
  1. requestInterceptors .foldRight({ r: Request -> r }) { f , acc -> f (acc ) }

参数 inital:R 的实参是 {r:Request->r} ,一个函数字面量,没有执行任何操作,接收 r 返回 r

参数 operation:(T,acc:R)->R 可接收一个 lambda,所以它的实参 {f,acc->f(acc)} 可以位于圆括号之外。 f 的泛型是 T ,具体类型是

                                                            
  1. (( Request) -> Request ) -> ((Request ) -> Request)

acc 的类型通过 initial:R 的实参 {r:Request->r} 可以推到得出—— (Request)->Request

OK,语法完全没毛病,再来看语义。

  1. +---------------------+

  2. | { r: Request -> r } | ---> 初始值,命名为 *fun0*

  3. +---------------------+

  4.           |







请到「今天看啥」查看全文