Kotlin高阶函数探究

如果一个函数接收另一个函数作为参数,或者返回类型是一个函数,那么这个函数我们就称之为高阶函数。任何以lambda或者函数引用作为参数的函数,或者返回值为lambda或函数引用的函数,都是高阶函数。

在参数中使用高阶函数示例:
/**
     * 加法
     */
    fun add(a: Int, b: Int): Int {
        return a + b
    }

    /**
     * 减法
     */
    fun minus(a: Int, b: Int): Int {
        return a - b
    }

    /**
     * 乘法
     */
    fun cheng(a: Int, b: Int): Int {
        return a * b
    }

/**
 * 运算
 *
 * operate:(Int, Int)->Int 表示定义的一个函数,将其作为calculate参数的形参,实际传的时候是传入add或minus函数
 */
fun calculate(a: Int, b: Int, operateType: (Int,Int) -> Int): Int {
        return operateType(a, b)
}

测试:

        Log.i("minfo", "" + calculate(12, 4, ::add))
        Log.i("minfo", "" + calculate(12, 4, ::minus))
        Log.i("minfo", "" + calculate(12, 4, ::cheng))

然后将add方法的变相写法:

/**
     * 将函数作为变量
     */
    val add1 = fun(a: Int, b: Int): Int {
        return a + b
    }
  
    //直接返回结果
    val add2 = fun(a: Int, b: Int) =  a + b

    //函数闭包写法
    val add3 = {a: Int, b: Int->
        a + b
    }

    val add4 = ::add
高阶函数作为返回值示例
//此方法的作用是如果type等于1,那么就从Num类中取出num进行乘法操作,如果不等于一,就进行相加操作
    fun getNum(type: Int): (Num) -> Int {
        if (type == 1) {
          //这是一种写法
            return { entity ->
                entity.num * entity.num
            }
        } else {
         //这是另一种简写,可以用it代替entity参数
            return {
                it.num + it.num
            }
        }
    }

首先定义了一个高阶函数,他传入三个参数,两个Int型的值和一个函数类型的值,在方法内部调用“函数类型的值”,因为它本身是函数,所以可以直接调用,并且将前两个Int型作为形参传了进去。接下来我们定义了两个函数add和minus,这两个函数实现他们本身的逻辑,最后在main函数里面调用了此高阶函数,其中::add和::minus是固定写法,表示函数的引用。

自己实现kolin自带高阶函数:

inline fun <T> Iterable<T>.myFilter(predicate:(T) -> Boolean): List<T> {
    var list = ArrayList<T>()

    for (item in this) {
        if (predicate(item)) {
            list.add(item)
        }
    }

    return list
}

inline fun <T> Iterable<T>.myForEach(handle:(T) -> Unit) {
    for (item in this) {
        handle(item)
    }
}

inline fun <T> T.myApply(handle:(T)-> Unit): T {
    handle(this)
    return this
}

inline fun <T, R> myWith(t: T, use:(T)-> R): R {
    return use(t)
}

inline fun <T, R> T.myRun(handle:(T) -> R): R {
    return handle(this)
}

测试使用:

ArrayList<User>().myApply {
            add(User("小王", 15))
            add(User("小李", 22))
            add(User("小张", 25))
        }.myFilter({
             it.age > 20
         }).myForEach({
             Log.i("minfo", it.name)
         })

        myWith(User("小王", 15)) {
            name = "哈哈"
            Log.i("minfo", name)
        }

        User("小王", 15).myRun {
            name = "修改小王为小花"
            Log.i("minfo", name)
        }

参考
Kotlin高阶函数

lambda(高阶函数)转成Java是怎么样的

将如下高阶函数转成java

    fun calculate(a: Int, b: Int, operateType: (Int,Int) -> Int): Int {
        return operateType(a, b)
    }

转如下代码:

   public final int calculate(int a, int b, @NotNull Function2 operateType) {
      Intrinsics.checkNotNullParameter(operateType, "operateType");
      return ((Number)operateType.invoke(a, b)).intValue();
   }

java中,则将高阶函数用了kotlin中一个接口Funtions来代替,接口仅有一个invoke方法,接口方法兼容了不同数量参数的高阶函数。

package kotlin.jvm.functions

/** A function that takes 0 arguments. */
public interface Function0<out R> : Function<R> {
    /** Invokes the function. */
    public operator fun invoke(): R
}
/** A function that takes 1 argument. */
public interface Function1<in P1, out R> : Function<R> {
    /** Invokes the function with the specified argument. */
    public operator fun invoke(p1: P1): R
}
/** A function that takes 2 arguments. */
public interface Function2<in P1, in P2, out R> : Function<R> {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2): R
}
...

高阶函数有几个参数,就使用对应几个参数的那个接口替换,并在java代码中调用invoke方法。

java调用kotlin的高阶函数是怎么调的

在java中调用如下高阶函数

    fun calculate(a: Int, b: Int, operateType: (Int,Int) -> Int): Int {
        return operateType(a, b)
    }

可以知道转为java之后函数参数变为了Functions类里面接口的类型,

   public final int calculate(int a, int b, @NotNull Function2 operateType) {
      Intrinsics.checkNotNullParameter(operateType, "operateType");
      return ((Number)operateType.invoke(a, b)).intValue();
   }

具体调用时,java调用需要传入Function接口类型参数,如这里是2个参数,传入Function2对象,需要自行实现invoke回调方法。如果该高阶函数只有一个参数,则这里是new Function1()

new LambdaUse().calculate(1, 2, new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer invoke(Integer integer, Integer integer2) {
                return null;
            }
        });
我理解的使用高阶函数的场景

在以前java里面需要将不确定的功能暴露给外部或者子类实现,可以使用接口或者抽象方法来调用,将实现交给外部。那么这个高阶函数就是实现同样功能的写法,用接口或者抽象方法来实现的地方,都可以改成高阶函数来做。

封装网络请求的例子就是一个很好的事例,如果不使用高阶函数,那么我们为了实现网络请求成功后的回调,还得额外多写一些接口类,得益于高阶函数,我们只需要这样即可:

    /**
     * 传入suspend方法发起请求,获取到BaseData数据,如果请求成功回调successCallBack函数并返回 T
     * 如果失败,调用failCallBack函数自行实现处理
     */
    fun <T: Any> requestData(
        showLoading: Boolean = true,
        request: suspend () -> BaseData<T>,
        successCallBack: (T) -> Unit,
        failCallBack: suspend (String) -> Unit = { errMsg->
            showLoading()
        }
    ) {
        viewModelScope.launch {
            if (showLoading) {
                showLoading()
            }
            try {
                val baseData = request.invoke()
                if (baseData.isSuccess()) {
                    baseData.data?.let { successCallBack(it) }
                } else {
                    baseData.errorMsg?.let { error(it) }
                }
            } catch (e: Exception) {
                e.message?.let { failCallBack(it) }
            } finally {
                if (showLoading) {
                    showLoading()
                }
            }
        }
    }

调用请求处理:

    fun loadData() {
        requestData(
            showLoading = true,
            request = { getUser() },
            successCallBack = { data ->
        
            })
    }

    suspend fun getUser(): BaseData<User> {
        return TODO("Provide the return value")
    }
Demo地址:

https://github.com/running-libo/HigherOrderFunction

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容