kotlin常见的高阶函数用法(一)

目录

  • 1.基础回调
  • 2.拓展函数
  • 3.方法覆盖
  • 4.方法重载
  • 5.泛型拓展

1.基础回调

kotlin支持把函数方法作为参数传递,而回调接口的定义不需要和java一样单独定义一个interface,可以随时定义随时使用

fun method1(targetBoolean: Boolean, targetMethod: (String) -> Unit) {
    if (targetBoolean) {
        targetMethod("成功");
    } else {
        targetMethod("失败")
    }
}

fun main() {
    val testTarget: (String) -> Unit = {
        println("target->$it")
    }
    method1(true, testTarget)
    method1(false, testTarget)
}

输出结果是

target->成功
target->失败

参数传递的方法定义基本格式 方法名:(参数1,参数2...) -> 返回类型,一般都要写完整

实现可以用lambda进行简化,比如下面的两种声明是同样的效果

fun main() {
    var method1: (Int, Int) -> Int = { num1, num2 -> num1 + num2 };
    var method2 = { num1: Int, num2: Int -> num1 + num2 };

    println("method1: ${method1(1, 2)}")
    println("method2: ${method2(1, 2)}")
}

此外,kotlin支持回调函数的声明,类似C++的声明函数,使用typealias关键字声明

typealias TargetM = (String) -> Unit

fun method2(targetBoolean: Boolean, targetMethod: TargetM) {
    if (targetBoolean) {
        targetMethod("成功2");
    } else {
        targetMethod("失败2")
    }
}

这个效果和上面的类似

当定义回调函数时,方法的实现体可以写在调用的方法之后,就是匿名函数的方式处理
比如上面定义的方法可以直接这么调用

fun main() {
  method2(true) { str ->
        print("target2 -> $str")
    }
    
  method2(true, { str ->
            print("target2 -> $str")
    })
 }

这两种其实是一样的效果,当回调参数是最后一个时,一般使用第一个的实现方式

2.拓展函数

kotlin支持对类的方法进行推展,拓展类未定义的方法,拓展本身支持自定义的或者android库中的类

class Student {
    val name = "s_name"
    val age = 10
}
fun Student.test() = println("name -> ${this.name} \nage -> ${this.age}")

fun File.aaa() = println("file extends path ->>${path} : ${exists()}")

fun main() {
     var stu = Student()
    stu.test()

    var file = File("E:/test.test")
    file.aaa()
}

上面我定义了一个类Student,然后对这个类进行方法拓展,可以获取到类中的属性,可以通过类对象直接调用;这个对系统类库同样适用,比如这里定义的File也是可以进行拓展的
输出结果是

name -> s_name 
age -> 10
file extends path ->>E:\test.test : false

同理比如我们定义

fun Context.toast(string: String) {
    Toast.makeText(this, string, Toast.LENGTH_LONG).show()
}

那么在所有有Context上下文环境的地方就可以直接使用这个方法,比如各个Activity中,十分灵活

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
		......
        toast("1234")
    }

3.方法覆盖

kotlin支持对局部变量回调的方法实现中支持对回调方法的重定义覆盖,可以使用之前的定义的参数属性(实测也无法重新定义入参,必须使用之前的入参),只需要覆盖方法实现

fun main() {
    var method = { num: Int -> println("method before $num") };
    method = { println("method after $it") }
    method(1);
}

输出结果是

method after 1

可以看出之前的方法确实被覆盖了,不会打印结果

4.方法重载

kotlin支持方法参数定义默认值,也支持定义重载方法,比如自定义View我们一般要实现三个构造方法

 public CustomView (Context context) {
        this(context,null);
    }
    public CustomView (Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }
    public CustomView (Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

而使用kotlin我们可以给方法定义重载的标记,下面的一个方法和上面的是等价的

class CustomView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr)

这个方法相当于定义了三个方法,其中第一个context是必传的,其他两个参数都有默认值
注意这里的参数顺序

5.泛型拓展

kotlin支持泛型的方法处理
可以看下applylet的实现方式

public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

可以看出这两个方法中都有T.method()的处理,这里可以理解是泛型参数的拓展方法,和上面提到的是一致的。而这里的this指的是拓展方法所指定的泛型对象。
apply这里调用了方法后返回了this,同时泛型返回也是同泛型,也就是说是可以链式调用的,方法内可以使用this.xxx方法获取相应的属性,当然this也是可以省略的,直接也是可以的。

class A {
    var a: String? = null;
    var b: String? = null;
}
fun main() {
    val A = A()
    A.apply {
        this.a = "1";
        a ="1.5"; //效果是一样的
    }.apply {
        this.b = "2";
    }
	
	//甚至可以层层套娃玩
	 A.apply {
        apply {
            apply { ... }
        }
    }
}

同时apply这里的block是也是一个类拓展函数,指定的是T.()的方法,其实这个就是指定将T作为方法的this上下文进行传递,然后匿名方法体实现中就可以直接通过this获取该对象了
比如我们定义一个匿名方法使用String.(),那么下面的使用是成立的,即使我们并没有指定apply的对象

fun apply2(block: String.() -> Unit) {
}
fun main() {
	apply2 {
        val lenth = this.length
        val str2 = this.substring(0, 3)
        val charArra = this.toCharArray()
    }
}

那么T.()作为匿名方法声明就可以类比上面的,就是把T作为this对象传递到匿名方法体中的。

let则是把泛型作为it传递的,block: (T)就是传递it上下文的操作了,因为泛型都是Tblock方法通过this传递了本身进去,这个就不举例了

那么把上面两个综合起来就可以看这么一个例子

fun <T, R> T.apply3(r: R, block: T.(R) -> R): R {
    return block(r)
}
fun main() {
	val str = "1234";
    str.apply3(123) {
        this.toInt() + it
        //toInt() + it 等价
    }.apply {
        print("apply result->>>$this")
    }
}

可以看出这里指定了两个泛型TR, 然后T.(R)这里把T作为this传参,通时R作为it传参到方法体中,也就是这里可以同时使用thisit进行操作
输出结果

apply result->>>1357

那么再加一个参数呢

fun <T, R, E> T.apply4(r: R, e: E, block: T.(R, E) -> R): R {
    return block(r, e)
}

fun main() {
	val str = "1234";
    str.apply4(123, "12") { v1, v2 ->
        toInt() + v1 + v2.toInt()
    }.apply {
        print("apply result->>>$this")
    }
}

因为it只支持本身,其实本身也是一种lambda声明,多个参数直接换成多个的格式就可以了,当然this就不用处理了,只需要处理传递进来的两参数即可
输出结果

apply result->>>1369

你可能感兴趣的:(kotlin)