java λ表达式_回调与Java8的λ表达式

【9.3.1 回调】中,介绍了回调和好莱坞原则

(Java中)回调与通常的(遵循OCP 代码的)非回调代码,使用的技术不过是动态绑定/多态。

回调与通常的非回调代码,从类图和其自身代码上,没有区别。

之所以需要回调callback、隐式调用Implicit invocation(某些软件架构的作者使用的术语),是因为分层结构的一条线的存在。

假定下层有IClient、Server:

package Lower;

@FunctionalInterface

public interface IClient {

public void callback(int i);// 参数为底层上传的数据

}

package Lower;

public class Server {

private IClient whoCallMe; //必须获得一个IXxx的引用,由构造器的参数提供

public Server(IClient event) {

whoCallMe = event;

}

public void copy() {

for(int i=0;i<100;i++){

if (i%10 == 0) {//在适当的时机调用回调

whoCallMe.callback(i/10);

}

}

System.out.println("copy() over");

}

}上层Client需要更新进度条——显示复制任务完成的进度时,需要按照下层接口IClient定义的方法callback,给出自己的实现。

package Upper;

import Lower.*;

public class Client implements IClient {

public void call() {

new Server(this).copy();//传递this

}

//下层调用时传回一些数据。

@Override

public void callback(int i) {

System.out.println("Upper:" + i + "0%");

}

public static void main(String[] a) {

//Server

new Client().call();

}

}一个回调函数/方法(简称回调/ callback)是上层模块实现的,将被下层模块(反过来)调用的方法。

3. 回调的实现

package Lower;

import java.util.List;

import java.util.ArrayList;

public class Server2 {

private List listeners = new ArrayList<>();//电话簿

public void register(IClient listener) {//监听器注册

listeners.add(listener);

}

public void copy() {

for (IClient x : listeners) {

for (int i = 0; i <= 100; i++) { //在适当的时机调用回调

if(i % 20 == 0) x.callback(i/10);// 通知所有已登记的演员

}

}

System.out.println("copy() over");

}

}

当下层模块状态发生某些变化时——通常由操作系统或JVM捕捉这种状态变化并调用回调函数,程序员最关心的是上层模块如何提供回调的方法体。最理想的方式是在注册时直接给出代码,如伪代码:

s.register(λi.(操作i)) //λ表达式

事实上,封装代码的callback(int)方法的方法名不需要存在(只需要参数和对参数的处理代码),更不用说封装callback(int)方法的类和对象。

还记得冯?诺依曼的存储-程序概念吗?可执行代码也被储存在内存中。从提供回调的方法体角度,在编程领域,

★回调通常指可以被作为参数传递给其他代码的可执行代码块,或者一个可执行代码的引用。

如果能够将可执行代码封装成方法如foo(),而方法名foo又可以作为其他方法的参数,则可以register(foo)实现回调。在JavaScript, Perl和 PHP中可以如此实现。

如果能够操作可执行代码的内存地址即函数指针(function pointers) 则可以像C或C++那样实现回调。

Java8的λ表达式,终于完成了回调的原意——代码的参数化,即doSth( foo )按照统一的形式,随着foo的不同使得doSth不同。

package Upper;

import Lower.*;

public class Client implements IClient {

public void call() {

new Server(this).copy();//传递this

}

//下层调用时传回一些数据。

@Override

public void callback(int i) {

System.out.println("Upper:" + i + "0%");

}

public static void main(String[] a) {

//Server2

// Server2 s =new Server2();

// s.register( new Client());

// s.register( new Client());

// s. copy();//这里由上层模块触发事件的发生

//3 λ表达式Vs. Java匿名类(参见9.4.5节)

Server2 s =new Server2();

IClient listener1=(i)->{System.out.println("+" + i + "0%");};

s.register(listener1);

s.register((i)->{System.out.println("++" + i + "0%");});

s.register(new IClient(){

@Override public void callback(int i){

System.out.println("==" + i + "0%");

}

});

s. copy();

}

}

为了方便地使用Lambda表达式取代匿名类,Java8新引入了概念:函数接口(functional interface),即仅仅显式声明了一个自己的抽象方法的接口(可以用@FunctionalInterface标注)。

IClient listener1=(i)->{System.out.println("+" + i + "0%");};

λ表达式的类型,叫做“目标类型(target type)”,必须是函数接口。上面的赋值语句,将λ表达式——事实上是函数接口的实现类的引用(也可以理解为像C或C++那样的函数指针)赋值给函数接口。

当然,有了锤子,满世界就有太多的钉子。有的的确是钉子,有的则被当成了钉子。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值