Java回调与Lambda表达式
2019更新:Java8的lambda函数式编程也来于此,只是要求接口中必须有且只有一个抽象方法,并且有个专用注解@FunctionalInterface 用于声明该接口为函数式接口
回调函数一般也称钩子函数
可能很多人对回调不是很熟悉,特别是做后端业务开发的同学,其实回调很常见,当你这样创建一个线程的时候就已经用到了回调
new Thread(new Runnable() {
@Override
public void run() {
System.out.println();
}
})
// 等价于lambda
new Thread(System.out::println);
我个人理解,在Java中回调就是多态的一种应用.
它一般通过接口,抽象类实现.
简单理解就是,你要调用的某个方法method_a有一个接口类型的参数Callback callback,method_a 方法内调用了callback内的方法method_b ,由于method_b 是抽象的,所以在你调用method_a的时候必须传一个Callback的实现类。当然你也可以不必专门去定义一个实现类,可以采用匿名内部类的方式(是不是有点绕?)
其实这样做的目的就是使代码更加灵活,可以在不同场景下定制化实现不同的处理逻辑,同时又能复用基础代码。
最常见的回调应用场景就是按钮事件处理,比如说setOnclicklistener这个方法(用来给按钮对象设置一个OnClickListener类型对象)
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO handle event
}
});
// 用lambda改写
btn.setOnClickListener(v -> {
//TODO handle event
});
事件多种多样,每个事件触发之后要执行的操作是不确定的,这时候就可以给按钮对象设置一个简单接口OnClickListener类型对象,在OnClickListener里面我们定义一个方法onClick,我们规定这个onClick方法就是用来消费事件的,按钮对象在监听到事件的时候会调用OnClickListener.onClick 方法。
这样尽管事件多种多样,都可以统一使用onClick这个方法去处理,具体怎么处理就看具体的onClick实现代码。所以这个onClick方法就叫回调方法,所以把它叫做钩子函数。
回调还经常出现在客户端访问网络的时候,安卓中规定在主线程中不可以直接访问网络等耗时操作,你应该放在子线程中去执行。不然主线程很容易被阻塞,主线程一旦被阻塞。UI就会出现假死,严重影响用户体验。(超过5秒未响应系统会提示长时间未响应)
为了应对这样的场景,我们做一个工具类HttpUtil,定义一个访问网络的方法getData(),专门用来处理网络请求。对于每次请求,getData() 里创建一个线程去异步访问网络,这样主线程就不会被阻塞。
但是子线程什么时候执行完,返回一个什么样的结果,这也是不确定的。但总会有一个结果,要么OK,要么error,要么timeout,所以你总得想办法handle一下。
这就就可以用上面说的回调来做,先定义一个HttpCallBack接口,可以把他定义在HttpUtil内部,在CallBack里定义onResponse、onFailure方法。
再在getData的参数中,定义一个HttpCallBack类型参数。如下:
public interface HttpCallBack {
void onResponse(JSONObject jsonObject);
void onFailure(String errorMsg);
}
在子线程处理完请求拿到结果的时候调用onResponse、onFailure。
这样一来,这个HttpUtil就是通用的了,你可以用他来下载一张图片,或者请求json数据。而你每次调用的时候都可以以匿名内部类方式实现HttpCallBack,在response里添加相应的数据处理逻辑,比如更新UI上的图片,解析得到的json数据并更新UI,或者在onFailure里弹出一个请求失败的提示对话框。
所以,在一些异步耗时操作或者需要定制化拓展的场景下回调比较常见。
Define:
public void getDate(List<Param> params, String url, final HttpCallBack httpCallBack) {
//new Thread
new Thread(new Runnable() {
@Override
public void run() {
//send request
//...
{
//if success
httpCallBack.onResponse(json);
//if failure
httpCallBack.onFailure(errorMsg);
}
}
}).start();
}
// lambda形式
public void getDate(List<Param> params, String url, final HttpCallBack httpCallBack) {
//new Thread
new Thread(() -> {
//send request
//...
{
//if success
httpCallBack.onResponse(json);
//if failure
httpCallBack.onFailure(errorMsg);
}
}).start();
}
Use:
getDate(params, url, new HttpCallBack() {
@Override
public void onResponse(JSONObject jsonObject) {
// parse jsonObject, refresh UI
}
@Override
public void onFailure(String errorMsg) {
alert(errorMsg);
}
});
注意:由于HttpCallBack 定义了两个抽象方法,不符合函数式接口的规定,无法用lambda改写!