Lambda表达式初步

介绍

什么是Lambda表达式?

在如 Lisp、Python、Ruby 编程语言中,Lambda 是一个用于表示匿名函数或闭包的运算符

为何需要lambda表达式?

  • 在 Java 中,我们无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法。
  • 在 JavaScript 中,函数参数是一个函数,返回值是另一个函数的情况是非常常见的;JavaScript 是一门非常典型的函数式语言。

看如下匿名内部类实例:

 import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; public class SwingTest {
public static void main(String[] args) {
JFrame jFrame = new JFrame("My JFrame");
JButton jButton = new JButton("My Button");
// 匿名内部类
jButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button Pressed");
}
});
jFrame.add(jButton);
jFrame.pack();
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

例 1:

如上代码很简单,通过第 10 行给第 8 行的按钮注册了一个监听器。而我们知道,当事件触发真正起作用的实际上是执行第 12 行的 actionPerformed 方法,为此我们创建了 ActionListener 实例。

而 Lambda 表达式可以让我们只专注于方法的实现,在注册监听器时我们可以忽略具体需要创建哪个类型的实例。其实在 Idea 中本身就已经给我们提示了,如下:

即匿名内部类的写法是可以用 Lambda 表达式替换的,如下:

 import javax.swing.*;

 public class SwingTest {
public static void main(String[] args) {
JFrame jFrame = new JFrame("My JFrame");
JButton jButton = new JButton("My Button");
// Lambda 表达式
jButton.addActionListener(event -> System.out.println("jButton Pressed"));
jFrame.add(jButton);
jFrame.pack();
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

例 2:

在例 2 中通过第 8 行的 Lambda 表达式替换了例 1 中 10-15 行的匿名内部类写法,不仅代码更简洁,而且含义更直观明了。

基本结构

在 Java 8 中 Lambda 表达式最原始的写法如下:

(类型 prarm1,类型 param2,类型 param3, ...) -> {
// 方法体
}

可以将参数类型省略,编译器可自动推断:

(prarm1, param2, param3, ...) -> {
// 方法体
}

如果只有一个参数,还可以省略小括号:

prarm1 -> {
// 方法体
}

如果方法体只有一行代码,那么大括号也可以省略:

prarm1 -> <方法实现>

用例 2 中的 Lambda 为例演变过程如下:

// 原始写法
jButton.addActionListener((ActionEvent event) -> {
System.out.println("jButton Pressed");
});
// 省略参数类型
jButton.addActionListener((event) -> {
System.out.println("jButton Pressed");
});
// 省略小括号
jButton.addActionListener(event -> {
System.out.println("jButton Pressed");
});
// 省略大括号
jButton.addActionListener(event -> System.out.println("jButton Pressed"));

函数式接口

介绍

在 Java 8 中, Iterable 接口中新增了一个默认函数 forEach 让我们可以更方便的遍历它:

 package java.lang;

 import java.util.Iterator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer; public interface Iterable<T> {
Iterator<T> iterator(); default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
} default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}

java.lang.Iterable

可以看到它接收一个 java.util.function.Consumer 类型参数,查看 JavaDoc:

该类上标注了一个 @FunctionalInterface 注解,并且注释中还说明了这个接口是一个函数式接口。查看该注解的 JavaDoc:

可以获得如下信息:

  • 这是一个信息性注解,用于标注在一个接口上使一个接口成为函数式接口。
  • 从概念上来说,一个函数式接口有且只能有一个抽象方法。
  • 如果在接口中仅定义了一个抽象方法并且它覆盖了 java.lang.Object 类中公开的方法,那么这个接口就不是函数式接口。
  • 函数式接口的实例可以通过 Lambda 表达式、方法引用或构造方法引用来创建。
  • 如果被该注解标注的类型不满足下面两个条件,那么编译器将生成错误信息。
    1. 该类型是一个接口,不可以是注解类型、枚举、类。
    2. 被注解类型必须满足函数式注解的要求。
  • 如果一个接口满足了函数式接口的要求,但它没有标注该注解,编译器也会将它看作是函数式接口。

Lambda表达式的作用

看如下示例:

package zze.java8;

@FunctionalInterface
interface MyInterface {
void test();
} public class Test2 {
public void test(MyInterface myInterface) {
System.out.println("Test2.test");
myInterface.test();
} public static void main(String[] args) {
Test2 test2 = new Test2();
test2.test(() ->System.out.println("MyInterface.test"));
/*
* Test2.test
* MyInterface.test
*/
}
}

可以看到, Test2 的实例方法 test 实际上是接收一个 MyInterface 对象,而 MyInterface 是一个函数式接口,所以我们可以通过传入 Lambda 表达式作为 myInterface 参数,即:此时传入的 Lambda 表达式就是对函数函数式接口 MyInterface 的实现对象。所以上面代码其实也可以修改为如下:

Test2 test2 = new Test2();
MyInterface myInterface = () -> System.out.println("MyInterface.test");
test2.test(myInterface);
System.out.println(myInterface.getClass());
System.out.println(myInterface.getClass().getSuperclass());
System.out.println(myInterface.getClass().getInterfaces()[]);
/*
Test2.test
MyInterface.test
class zze.java8.Test2$$Lambda$1/990368553
class java.lang.Object
interface zze.java8.MyInterface
*/

总结:

  • Lambda 表达式为 Java 添加了缺失的函数式编程特性,使我们能将函数当做一等公民看待。
  • 在将函数作为一等公民的语言中(如 Python、JavaScript),Lambda 表达式的类型是函数。但在 Java 中,Lambda 表达式是一个对象,它们必须依附于一类特别的对象类型——函数式接口(functional interface)。

Java8(1)之Lambda表达式初步与函数式接口的更多相关文章

  1. java8学习之Lambda表达式初步与函数式接口

    对于Java8其实相比之前的的版本增加的内容是相当多的,其中有相当一大块的内容是关于Lambda表达式与Stream API,而这两部分是紧密结合而不能将其拆开来对待的,但是是可以单独使用的,所以从学 ...

  2. java8学习之Lambda表达式继续探讨&Function接口详解

    对于上次[http://www.cnblogs.com/webor2006/p/8186039.html]已经初步引入的Java8中Stream流的概念,其中使用了map的操作,它需要接受一个Func ...

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

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

  4. 怒学Java8系列一:Lambda表达式

    PDF文档已上传Github  Github:https://github.com/zwjlpeng/Angrily_Learn_Java_8 第一章 Lambda 1.1 引言 课本上说编程有两种模 ...

  5. 乐字节-Java8新特性-Lambda表达式

    上一篇文章我们了解了Java8新特性-接口默认方法,接下来我们聊一聊Java8新特性之Lambda表达式. Lambda表达式(也称为闭包),它允许我们将函数当成参数传递给某个方法,或者把代码本身当作 ...

  6. java8新特性——Lambda表达式

    上文中简单介绍了一下java8得一些新特性,与优点,也是为本次学习java8新特性制定一个学习的方向,后面几篇会根据上文中得新特性一一展开学习.本文就从java8新特性中比较重要的Lambda表达式开 ...

  7. JAVA8学习——深入浅出Lambda表达式(学习过程)

    JAVA8学习--深入浅出Lambda表达式(学习过程) lambda表达式: 我们为什么要用lambda表达式 在JAVA中,我们无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法. 在 ...

  8. Java8 新特性lambda表达式(一)初始

    本篇参考Richard Warburton的 java8 Lambdas :Functional Programming for the Masses 学习lambda表达式之前,需要知道什么是函数式 ...

  9. Java8学习笔记----Lambda表达式 (转)

    Java8学习笔记----Lambda表达式 天锦 2014-03-24 16:43:30 发表于:ATA之家       本文主要记录自己学习Java8的历程,方便大家一起探讨和自己的备忘.因为本人 ...

随机推荐

  1. PHP输出缓存ob系列函数

    ob,输出缓冲区,是output buffering的简称,而不是output cache.ob用对了,是能对速度有一定的帮助,但是盲目的加上ob函数,只会增加CPU额外的负担. ob的基本原则:如果 ...

  2. python内置模块之-logging

    logging 模块主要用于写日志 logging模块主要有如下几个组件 Logger  Logger对象提供应用程序可直接使用的接口 Handler Handler发送日志到适当的目的地 Filte ...

  3. 剑指offer数组1

    面试题3:数组中重复的数字 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例 ...

  4. ssh报错 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

    今天登陆远程主机的时候,出现如下的报错信息 ssh 10.0.0.1 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WAR ...

  5. Linux安装Tomcat-Nginx-FastDFS-Redis-Solr-集群——【第五集之补充-转载“深入理解VMware虚拟网络”】

    郑重声明,此文太好,按耐不住要保存起来好好研究研究,如果侵权,联系我. 转载自王春海的http://blog.51cto.com/wangchunhai/381225,有所更改. 同时可以参考:htt ...

  6. antd中按需加载使用react-app-rewired报错

    [描述] 按照antd官网步骤 https://ant.design/docs/react/use-with-create-react-app-cn 最后yarn start会报错 [解决方法] 原因 ...

  7. MFC开发(一)简单同步时间应用程序

    看了一个垃圾程序的架构,mmp真坑,自己费了一点功夫才搞定,就直接记录下吧,这个是windows简单的应用程序,但是里面有点复杂,我们需要首先建立一个基于mfc的appwinzard程序,(凭记忆写的 ...

  8. C#中new的三种用法

    在 C# 中,new 关键字可用作运算符.修饰符或约束. 1)new 运算符:用于创建对象和调用构造函数. 2)new 修饰符:在用作修饰符时,new 关键字可以显式隐藏从基类继承的成员. 3)new ...

  9. XIV Open Cup named after E.V. Pankratiev. GP of Europe

    A. The Motorway 等价于找到最小和最大的$L$满足存在$S$使得$S+(i-1)L\leq a_i\leq S+i\times L$ 即 $S\leq\min((1-i)L+a_i)$ ...

  10. Java 多线程 sleep()方法与yield()方法的区别

    sleep()方法与yield()方法的区别如下: 1 是否考虑线程的优先级不同 sleep()方法给其他线程运行机会时不考虑线程的优先级,也就是说,它会给低优先级的线程运行的机会.而yield()方 ...