什么是Lambda Expression

对于Lambda Expression,我的理解是,它是一个函数表达式,如下:

(int x, int y) -> x - y

符号左边定义了函数的输入,右边定义了输出。在上面的例子中,我们定义了一个关于x,y的函数,这个函数的作用是计算两者之差。那么它的类型是什么呢?比如1+1这个表达式它的(返回值)类型是int。对于函数表达式,它的类型一般是由上下文定的。

把上面的例子放到具体使用环境中,如下所示,我们可以根据上下文语义判断,该Lambda Expression的(返回值)类型是Comparator。

Arrays.sort(input[], (int x, int y) -> x - y );

为什么要引入Lambda Expression

Lambda Expression是Java 8引入的一个非常重要的新特性,它也是Java第一次尝试引入函数式编程的相关内容。

关于函数式编程,我想类比javascript,在js中函数也可以作为变量的基本类型之一,并且可以当做值一样进行传递。但是在Java中,函数是绑定在对象上的,如果需要调用一个函数foo(),需要先实例化一个对象Obj,然后再调用这个对象里的该函数Obj.foo()。如果Java中可以像js一样,直接传递函数,岂不是很酷?

为了实现这个功能,用到了:
- Functional Interface 函数式编程的概念
- Java8新增的接口 Function
- Java8新增的语法 Lambda Expression

具体怎么实现呢?本质上来说是,新创建一个对象,然后实现Function接口。这个对象即可以看成是一个函数。

除此之外,Lambda Expression还可以快速地创建匿名对象并实现接口方法。

下面,对这两种模式具体讲解。

Lambda Expression的使用 - 创建对象

在面向接口的编程中,我们会遇到许多函数式接口(Functional Interface),即只包含一个抽象方法的接口,比如java.lang.Runnable和java.util.Comparator都是典型的函数式接口。在实际使用过程中,我们需要新建一个对象,然后implements这个接口,再实现该接口的抽象方法。如下所示:

// 常规方法
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
log(e.getActionCommand());
}
});

这段代码实现的功能是给这个button加一个ActionListener,然后在这个button有操作的时候,log该操作的名称。这样写起来有点复杂,实际上我们真正关心的只是那一行log的代码而已。如果使用Lambda Expression,可以大大简化代码量,如下所示:

// Lambda Expression方法
button.addActionListener(e -> log(e.getActionCommand()));

下面,再给出一个经典的Comparator的例子参考。

// 常规方法
Arrays.sort(input[], new Comparator<Integer>() {
@Override
public int compare(int a, int b) {
return (a-b); // in ascending order
}
}); // Lambda Expression方法
Arrays.sort(input[], (int a, int b) -> a-b ); 

下面,再给出一个常用的Runnable的例子参考。

Thread thread = new Thread(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
});
thread.start();

Lambda Expression的使用 - 传递函数

前面已经提到过了,Java不能传递函数,只能传递对象。为了实现传递函数的功能,设计上采取了一个折中的方法,创建了一个Function的接口。这样一来需要被传递的函数就可以作为一个新的对象,实现这个Function接口,来进行传递。如下示例:

在该例中,(String v) -> v.length()这个Lambda表达式就是传递的方法(函数)。

public class CollectionUtils {
  // 对传入的参数input进行processor操作,依旧以list的形式返回
  public static List map(List input, Function processor) {
    ArrayList result = new ArrayList();
    for (T obj : input) {
      result.add(processor.apply(obj));
  }
  return result;
} public static void main(String[] args) {
  List input = Arrays.asList(new String[] {"apple", "orange", "pear"});
  // 统计字符串的长度
  List lengths = CollectionUtils.map(input, (String v) -> v.length());
  // 将字符串转换为大写
  List uppercases = CollectionUtils.map(input, (String v) -> v.toUpperCase());
}

下面,再给出一个Callable的例子。例中,delayFeedback("task", 1)就是传递的方法(函数)。

Callable<String> delayFeedback(String feedback, long sleepSeconds) {
return () -> {
TimeUnit.SECONDS.sleep(sleepSeconds);
return feedback;
};
} ExecutorService executor = Executors.newWorkStealingPool(); List<Callable<String>> tasks = Arrays.asList(
delayFeedback("task1", 1),
delayFeedback("task2", 2),
delayFeedback("task3", 3)); String result = executor.invokeAny(tasks);

反思与总结

从上面的例子中,我们大概对Lambda Expression是什么,怎么使用有了一个大概的了解。

回到最初的问题,再次反思Lambda Expression到底是个什么东西。我觉得,首先,可以把它看作一个Anonymous Class。其次,这个Class一定继承了某个Function Interface。至于这Class是什么类型的,需要根据上下文判断。

那么,什么地方可以用Lambda Expression?答案是FunctionalInterface。什么是FunctionalInterface?官方的回答是:

Conceptually, a functional interface has exactly one abstract method.

举几个常见的FunctionalInterface的例子:

@FunctionalInterface
public interface Comparator<T> @FunctionalInterface
public interface Function<T,R> @FunctionalInterface
public interface Predicate<T> public Interface ActionListener

这正好对应了文中之前出现的例子。(说明:对于第1,2,3个,Java 8 API已经用Annotation标明了它是FunctionalInterface,对于第4个,虽然没有标,但是它符合FunctionalInterface的定义,所以也是。)

链接

浅析Java 8新特性Lambda Expression的更多相关文章

  1. Java 8 新特性 - Lambda表达式

    Lambda表达式 vs 匿名类既然lambda表达式即将正式取代Java代码中的匿名内部类,那么有必要对二者做一个比较分析.一个关键的不同点就是关键字 this.匿名类的 this 关键字指向匿名类 ...

  2. 浅析Java 8新特性Method Reference

    什么是方法引用 我们知道了什么是Lambda Expression以及如何使用,那么,Method References又是什么呢?Oracle Java Docs中这样说: They are com ...

  3. Java 8新特性----Lambda

    Lambda 一.如何辨别Lambda表达式 Runnable noArguments = () -> System.out.println("Hello World"); ...

  4. Java 8新特性--Lambda表达式作为返回值

    lambda表达式作为方法的返回值:

  5. Java 8 新特性--Lambda表达式作为方法参数

    Lambda表达式的使用场景: 当方法的参数是一个函数式接口时,可以使用Lambda表达式进行简化—— 首先,前提是Runnable接口是一个函数式接口,经过查看源码得知,确实如此: 将Runnabl ...

  6. Java 12 新特性介绍,快来补一补

    Java 12 早在 2019 年 3 月 19 日发布,它不是一个长久支持(LTS)版本.在这之前我们已经介绍过其他版本的新特性,如果需要可以点击下面的链接进行阅读. Java 11 新特性介绍 J ...

  7. Java 8 新特性:Lambda、Stream和日期处理

    1. Lambda 简介   Lambda表达式(Lambda Expression)是匿名函数,Lambda表达式基于数学中的λ演算得名,对应于其中的Lambda抽象(Lambda Abstract ...

  8. Java8新特性-Lambda表达式是什么?

    目录 前言 匿名内部类 函数式接口 和 Lambda表达式语法 实现函数式接口并使用Lambda表达式: 所以Lambda表达式是什么? 实战应用 总结 前言 Java8新特性-Lambda表达式,好 ...

  9. Java 8 新特性——检视阅读

    Java 8 新特性--检视阅读 参考 Java 8 新特性--菜鸟 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的 ...

随机推荐

  1. xml对象序列化

    public static class XSerializer { /// <summary> /// 将对象序列化为xml字符串 /// </summary> /// < ...

  2. valgrind安装与使用

    1.得到源代码:http://valgrind.org/->source code 下载最新版的valgrind得到:valgrind-3.2.3.tar.bz2 2.解压安装包:tar -jx ...

  3. [bzoj]2962序列操作

    [bzoj]2962序列操作 标签: 线段树 题目链接 题意 给你一串序列,要你维护三个操作: 1.区间加法 2.区间取相反数 3.区间内任意选k个数相乘的积 题解 第三个操作看起来一脸懵逼啊. 其实 ...

  4. Egret学习笔记.1 (写在前面的废话)

    我记得之前谁说过,大部分程序员入行,都是因为小的时候的游戏机啊,各种电子设备啊....觉得有意思,才入的行 . 至少我本人是因为之前上高中那会儿,喜欢玩手机.那会儿还是MTK,塞班的时代,喜欢拿着手机 ...

  5. python资源推荐

    一.文档教程 1. 廖雪峰python教程 廖老师的教程我相信不用说了吧,每个学习python的人或多或少都听说过他,对我的帮助很大. 2.python中文学习大本营 名字叫做python中文学习大本 ...

  6. 原创:实现ehcache动态创建cache,以及超期判断的具体逻辑

    当前最常用的三个缓存组件:ehcache.redis.memcached 其中,ehcache与应用共同运行于JVM中,属于嵌入式组件,运行效率最高,因此常被用于实现一级缓存. 在更复杂的一些系统中, ...

  7. HDU - 3567 Eight II (bfs预处理 + 康托) [kuangbin带你飞]专题二

    类似HDU1430,不过本题需要枚举X的九个位置,分别保存状态,因为要保证最少步数.要保证字典序最小的话,在扩展节点时,方向顺序为:down, left, right, up. 我用c++提交1500 ...

  8. Nginx 调优经验记录

    1.2017年连续爆出5.x版本xshell安全问题和intel的cpu设计漏洞 ,此时我就注意到尽量少暴露自己线上使用的工具以及版本.例如:mysql版本,以及缓存层策略,服务器版本等,以下为 隐藏 ...

  9. 有状态InheritableThreadLocal 配合 JDK8 ,异步方法调用

    我们可以把一个类的作用域注解为 @Scope(scopeName = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode. ...

  10. Centos7下,简单DOCKER 使用.映射SSH端口到宿主主机.

    其实使用docker完全没有必要ssh,初学的时候,可以这样熟悉以下操作. 参考这哥们的文章:http://www.jianshu.com/p/d2dd936863ec 获取镜像 docker pul ...