• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

高阶函数-Kotlin

武飞扬头像
精神小伙JUMP
帮助1

Lambda表达式和匿名函数

一、函数类型

1.1、函数类型定义

Kotlin使用类似(Int) → String 的函数类型来处理函数的声明

声明规则:

  • 函数类型都有一个圆括号括起来的参数类型列表以及一个返回类型:形如(A, B) -> C 表示接受的类型分别为AB 两个参数并返回C 类型值的函数类型。参数类型列表可以为空,如:() -> CUnit 返回类型 不可省略

  • 函数类型可以有一个额外的接收者类型,声明规则是在上述函数声明前面加接收者类型. 例如

    A.(B) -> C 。带接收者的函数和普通函数的区别主要有如下两点

    • 除了可以和普通函数一样的方式调用(接收者对象作为普通函数的第一个参数)外,还可以通过接受者类型对象.(参数1,参数2,…)即A.(B) 的方式调用
    • 在函数体内,传给调用的接受者对象成为隐式的this,以便访问接收者对象的成员而无需任何额外的限定符,亦可使用this关键字访问接受者对象
  • 挂起函数属于函数类型的特殊种类,它的声明中有一个suspend 修饰符,例如suspend() → Unit 或者suspend A.(B) → C

函数类型声明可以选择性地包含函数的参数名: (x:Int, y:Int) → Point 。这些名称可以用于表示参数的含义。

函数类型可以使用圆括号进行接合: (Int) → ((Int) → Unit) 等价于(Int) → (Int) → Unit但是不等价于((Int) → Int) →Unit

1.2、函数类型实例化

函数字面值:指不声明而是直接作为表达式传递的函数,lambda表达式和匿名函数都是函数字面值

  • 使用字面值的代码块,即以下形式之一

    • lambda表达式:{a, b → a b }
    • 匿名函数:fun(a:Int, b:Int): Int { return a b }

    带接收者类型的函数字面值可用作实例化带有接收者的函数类型

  • 使用已有声明的可调用引用

    • 顶层、局部、成员、扩展函数;示例:::isOdd、String::toInt
    • 顶层、成员、扩展属性:List<Int>::size (???这一点说实话没太理解)
    • 构造函数:::Regex

    包括指向特定实例成员的绑定的可调用引用:foo::toString

  • 使用实现函数类型接口的自定义类的实例

//函数类型可以当成接口实现
class Sum : (Int, Int) -> Int {
    override fun invoke(p1: Int, p2: Int): Int {
        return p1   p2
    }
}
val sum: (Int, Int) -> Int = Sum()
//调用
val result = sum.invoke(2,2)
//或者当成函数调用
val result = sum(2,2)

带与不带接收者的函数类型非字面值(不是将函数直接当成参数传递)可以互换,其中接收者可以替代第一个参数,反之亦然。例如,(A, B) → C类型的值可以赋值给期待A.(B) → C类型值的地方,反之亦然

val double: (Int) -> Int = { x -> x * x }

val threeTimes: Int.() -> Int = { this * this * this }

fun calculate(x: Int, cal: (Int) -> Int): Int {
    return cal(x)
}
fun calculate2(x: Int, cal: Int.() -> Int): Int {
    return cal(x)
}

fun test(): Int {
    val value1 = calculate(100, double)      //OK value1=10000
    val value2 = calculate2(100, double)     //OK value2=10000
    val value3 = calculate(100, threeTimes)  //OK value3=1000000
    val value4 = calculate2(100, threeTimes) //OK value4=1000000

		//字面值是不可以互换的
    //calculate(100, { this * this * this }) //build fail
    //calculate2(100, { x -> x * x })        //build fail
}

默认情况下推断出的是没有接收者的函数类型,即使变量是通过扩展函数引用来初始化的。如果需要接收者方式调用,需要显式指定函数类型

//注意这个要写在顶层函数里,局部函数无法通过 类型::方法名的方式引用
fun Int.realNameDouble2(): Int {
    return this * this
}

class xxx{
	fun realNameDouble(x: Int): Int {
        return x * x
    }

   val double2: Int.() -> Int = ::realNameDouble

   val double3 = Int::realNameDouble2

	 fun test() {

        100.double2()
        100.double3() //编译错误,不能通过这种方式调用
        double3(100) 
        100.realNameDouble2()
    }
}

2、高阶函数

高阶函数是将函数用作参数或者返回值的函数

//示例:
fun calculate(x: Int, cal: (Int) -> Int): Int {
        return cal(x)
}

上述示例,除了传入正常的参数x ,还有一个参数cal 它的类型就是函数类型(Int) -> Int。因此calculate函数就是一个高阶函数,可以接收一个函数类型的实例作为参数

//调用示例
fun main() {
	  //传入一个lambda表达式作为函数实例
		////如果函数参数在最后一个,lambda表达式作为参数传入时可以放在圆括号之外
    calculate(100) { x -> x   x } 
}

二、高阶函数运用

2.1、官方自带高阶函数分析

高阶函数 let、apply、also、with 分析

2.2、自定义高阶函数—多参数判空

我们经常会遇到一个场景,需要同时对多个参数进行判空,任一参数为空则不继续执行

在Java中需要一个个参数判断,如果参数很多代码很长而且复杂,可以写一个多参数判空的Kotlin的高阶函数来简化该逻辑

inline fun <T, R> notNull(vararg args: T?, block: () -> R): R? {
    val filter = args.filterNot { it == null }
    val size = filter.size
    if (filter.size == args.size) {
        return block()
    }
    return null
}

//测试一下
var string1 = "1"
var string2 = "2"
fun main() {
    notNull(string1, string2) {
         //参数均不为空,继续执行
    }
}

2.3、自定义高阶函数—替换接口

比如有这个一个场景,我们需要通过标识查看有没有创建对象,没有创建对象时再去创建。

具体比如Fragment的创建,因为方法可能会被多地方调用创建不同的Fragment。

按照之前的写法我们不想在不需要的时候的创建Fragment实例,要么只能分开在不同的方法中写相似的逻辑;要么只能通过接口回调的方式实现需要时再创建Fragment实例,将这个场景抽象的同一个方法中

但是通过高阶函数我们可以直接通过一个方法实现,也无法新增接口;实例如下:

private fun showFragment(tag: String, create: () -> Fragment) {
        
        val fragment = supportFragmentManager.findFragmentByTag(tag)
        if (fragment?.isAdded == true) {
            return
        }
        supportFragmentManager.commit(true) {
            setReorderingAllowed(true)
            val f = create()
            replace(R.id.mainFragmentContainer, f, f.javaClass.name)
        }
    }

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgahjje
系列文章
更多 icon
同类精品
更多 icon
继续加载