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")
})
}
这两种其实是一样的效果,当回调参数是最后一个时,一般使用第一个的实现方式
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")
}
kotlin支持对局部变量回调的方法实现中支持对回调方法的重定义覆盖,可以使用之前的定义的参数属性(实测也无法重新定义入参,必须使用之前的入参),只需要覆盖方法实现
fun main() {
var method = { num: Int -> println("method before $num") };
method = { println("method after $it") }
method(1);
}
输出结果是
method after 1
可以看出之前的方法确实被覆盖了,不会打印结果
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
是必传的,其他两个参数都有默认值
注意这里的参数顺序
kotlin支持泛型的方法处理
可以看下apply
和let
的实现方式
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
上下文的操作了,因为泛型都是T
,block
方法通过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")
}
}
可以看出这里指定了两个泛型T
和R
, 然后T.(R)
这里把T
作为this
传参,通时R
作为it
传参到方法体中,也就是这里可以同时使用this
和it
进行操作
输出结果
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