回调与Java8的λ表达式

【9.3.1 回调】中,介绍了回调和<a target=_blank href="http://blog.csdn.net/yqj2065/article/details/8758101">好莱坞原则

(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<IClient> 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++那样的函数指针)赋值给函数接口。



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

回调与Java8的λ表达式

时间: 2024-10-25 23:43:26

回调与Java8的λ表达式的相关文章

Java8 Lambda表达式应用案例 -- 单线程游戏服务器+异步数据库操作

前段时间我们游戏服务器的开发环境升级到了Java8,这两天我又把服务器的线程模型重新设计了一下,用上了Lambda表达式.Lambda表达式确实能够大幅简化Java代码,特别是丑陋不堪的匿名内部类,这篇文章主要就是想和大家分享这一点. 线程模型 首先简单介绍一下我们游戏服务器的线程模型,大致如下图所示: Netty线程池只处理消息的收发,当Netty收到消息之后,会交给游戏逻辑线程处理.由于是单线程在处理游戏逻辑,所以每一个消息必须很快处理完,也就是说,不能有数据库等耗时操作,不然逻辑线程很可能

Java8 Lambda表达式深入学习(4) -- Java8实现方式

前几篇文章讨论了函数式接口和Lambda表达式语法.invokedynamic指令,以及Groovy2如何利用indy指令.本篇文章在前面几篇的基础之上,简要介绍Java8底层是如何实现Lambda表达式的. 示例代码 本文将以下面的代码为例展开讨论: import java.util.Arrays; import java.util.List; public class LambdaImplTest { public static void main(String[] args) { m1(A

java8 lambda表达式初接触

环境是jdk8 代码如下: package memTest; /** * Created by PC on 2014/8/5. */ public class LambdaOne { public static void test(){ new Thread(()-> System.out.print("hi") ).start(); } public static void main(String[] args) { new LambdaOne().test(); } } ja

深入学习Java8 Lambda表达式 -- 从函数式接口说起

希望本文能够成为Java8 Lambda表达式的快速入门指南. 函数式接口 理解Functional Interface(函数式接口,以下简称FI)是学习Java8 Lambda表达式的关键所在,所以放在最开始讨论.FI的定义其实很简单:任何接口,如果只包含唯一一个抽象方法,那么它就是一个FI.为了让编译器帮助我们确保一个接口满足FI的要求(也就是说有且仅有一个抽象方法),Java8提供了@FunctionalInterface注解.举个简单的例子,Runnable接口就是一个FI,下面是它的源

Java8 Lamdba表达式 003

继续lamdba.上篇Java8 Lamdba表达式 002讲了使用lamdba对集合元素进行排序,本篇讲述使用lamdba对集合元素过滤,演示样例代码沿用上篇: 001 public class SortingPlayer { public static void main(String[] args) { List<Player> playerList = new ArrayList<>(); playerList.add(new Player("Black"

Java8 Lambda表达式深入学习(2) -- InvokeDynamic指令详解

为了更好的支持动态类型语言,Java7通过JSR292给JVM增加了一条新的字节码指令:invokedynamic.之后,JVM上面的一些动态类型语言,比如Groovy(2.0+)和JRuby(1.7.0+)都开始支持invokedynamic.不过让人意外的是,为动态语言量身定制的invokedynamic指令,居然也被用到了Java8的Lambda表达式(JSR335)实现上.本文会对invokedynamic(以下简写做indy)指令做出详细解释. 测试代码 Java7以及更早版本的Jav

Java8 Lambda表达式详解手册及实例

先贩卖一下焦虑,Java8发于2014年3月18日,距离现在已经快6年了,如果你对Java8的新特性还没有应用,甚至还一无所知,那你真得关注公众号"程序新视界",好好系列的学习一下Java8的新特性.Lambda表达式已经在新框架中普通使用了,如果你对Lambda还一无所知,真得认真学习一下本篇文章了. 现在进入正题Java8的Lambda,首先看一下发音 ([?l?md?])表达式.注意该词的发音,b是不发音的,da发[d?]音. 为什么要引入Lambda表达式 简单的来说,引入La

Java8 Lambda表达式教程

1. 什么是λ表达式 λ表达式本质上是一个匿名方法.让我们来看以下这个样例: public int add(int x, int y) { return x + y; } 转成λ表达式后是这个样子: (int x, int y) -> x + y; 參数类型也能够省略,Java编译器会依据上下文判断出来: (x, y) -> x + y; //返回两数之和 或者 (x, y) -> { return x + y; } //显式指明返回值 可见λ表达式有三部分组成:參数列表.箭头(->

Java8 Lambda表达式入门

Lambda表达式的实质就是一个匿名函数.C#3.0引入了Lambda表达式,Java8也不甘示弱.Java8发布很久了,今天安装了JDK体验了Java8中的Lambda表达式. 首先看一个不适用Lambda表达式的例子. 比如我们要对一组字符串进行排序. public class Hello { public static void main(String[] args) { List<String> names = Arrays.asList("Tan", "