Kotlin高阶函数、扩展函数

高阶函数

首先理解一下函数类型这个东西,在kotlin中,函数和Int、String等一样,也是一种类型,也有相应的对象,意味着可以声明变量类型或者作为方法的参数/返回类型。函数类型和扩展函数不一样!扩展函数相当于只是在原有的类中新增了一个方法,但它并不是一种类型。

函数类型的格式(就是把一个普通函数的参数、返回类型提取了出来):

// (基本类型, 基本类型,....) -> 基本类型
// 例如
(Int, String) -> Boolean

声明变量并赋值,这个变量的值可以被改变,但值一定是个函数:

var func: (Int) -> String = { age: Int -> "age = $age" }

作为函数的参数,调用函数类型时直接使用.invoke(),或者直接调用函数:

testFunc(func)

fun testFunc(block: (Int) -> String) {
    block.invoke(18)
    // block(18) 直接调用也可以
}

作为函数的返回值:

fun testFunc(): (Int) -> String {
    return func
}

这样参数或者返回值是个函数类型的函数,例如testFunc()就是一个高阶函数。

有一个混淆的概念也要提一下,那就是匿名函数,格式如下:

// fun(基本类型):基本类型{
// }
// 例如
fun(age:Int):String{
	return "age = $age"
}

和普通方法的区别就是没有方法名,但是注意,它是一个函数对象,它不是一个函数!正是因为它是一个函数类型的对象,所以才可以被赋值给变量后者传参。

// 作为参数传给testFunc方法
testFunc(
	fun(age:Int):String{
		return "age = $age"
	}
)

同时也意味着它不能作为顶层函数,也不能作为一个普通函数写在类里面,会报错的。

有时候会看到这样的写法:

testFunc(::func)

fun func(age:Int):String{
		return "age = $age"
}

这里的::func也是一个函数对象,一个类型为func的对象,作为参数传给了testFunc(),也可以赋值给变量或者直接调用:

// 赋值
val f = ::func

// 直接直接调用
(::func).invoke()
f.invoke()

fun func(age:Int):String{
		return "age = $age"
}

现在对比一下函数类型和普通类型,就更好理解了

普通类型函数类型
声明变量例如val s: String例如val f: (String)->Unit
对象赋值s = “你好”f = { name -> }
作为传参fun test(str: String){}
test(s)
fun test(block: (String)->Unit){}
test(f)
作为返回值fun test():String{
return s
}
fun test():(String)->Unit{
return f
}
调用它的方法s.length()f.invoke()

也就是说函数是函数,函数类型是函数类型,函数对象是函数对象,三者是不一样的。

扩展函数

有时候会遇到这样的写法:

fun testFunc(block: T.()->Unit){}

T.() -> Unit和普通类型()-> Unit有什么区别呢,首先他们都是函数类型,不同的地方:()-> Unit是普通函数,可以在任何地方直接调用invoke,T.()->Unit是一个带接收者的扩展函数,必须有一个接收者才能调用,先看普通类型:

fun testFunc(block: ()->Unit){
    // 调用block
    block.invoke()
}

再看扩展类型:

fun testFunc(block: T.()->Unit){
    // 必须有个接收者来调用
    val t = T()
    t.block()
		// 或者
    block.invoke(t)
}

值得一提的是,T.(X, X, ....)->Unit(T, X, X, ....)->Unit作为形参时是等价的,可以替换使用:

fun main() {
    testFunc(a) // 注意这里a的类型
}

fun testFunc(block: String.(Int) -> Char) {
    "hello".block(18)
    block.invoke("hello", 18)
}

val a: (String, Int) -> Char = String::get

在Java中调用

先来看高阶函数,例如:

fun testFunc(block: (Int) -> String) {
    block.invoke(18)
}

block参数是一个函数类型,被编译成Function接口的子接口,根据入参的个数会被编译成Funtion0、Function1、Function2等等。这些Function接口只有一个抽象方法invoke,入参类型和函数类型中的入参类型一致,出参也是一致的,也就是说,block参数类型等价于FuntionN类型,在Java中调用时,直接传入匿名内部类即可,也可以用lambda表达式。

在Java中调用编译器会给出提示,完整的调用方式如下:

TestKt.testFunc(new Function1<Integer, String>() {
    @Override
    public String invoke(Integer integer) {
        return null;
    }
});

再来看扩展函数,看这个例子:

fun testFunc(block: String.(Int) -> Char) {
}

上面提到过T.(X, X, ....)->Unit(T, X, X, ....)->Unit是等价的,那么这个函数等价于:

fun testFunc(block: (String, Int) -> Char) {
}

其实又回到了上面的高阶函数调用方式,是一样的调用方式:

TestKt.testFunc(new Function2<String, Integer, Character>() {
    @Override
    public Character invoke(String s, Integer integer) {
        return null;
    }
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值