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. 【python】命令行神器 Click 简明笔记

    全文拷贝自 命令行神器 Click 简明笔记 Click Click 是用 Python 写的一个第三方模块,用于快速创建命令行.我们知道,Python 内置了一个 Argparse 的标准库用于创建 ...

  2. sort实现ip排序

    说明: 工具:sort linux自带        ”-t” : 表示以那个字符做分割        “-k” :和-t结合使用,表示取那一段为关键字进行排序,后面跟数据,1…n,表示取第几段,也可 ...

  3. 用PHPExcel导出导入Excel

    thinkPHP5.0框架 查询数据库调用Excel方法 public function exportlist(){ $orderModel = new OrderModel(); if($start ...

  4. using eclipse to write c programe

    参考:http://developer.51cto.com/art/200906/126363.htm http://www.cnblogs.com/feisky/archive/2010/03/21 ...

  5. 2017-10-22—发光二极管

    LED与 LD的差别在工作原理上的差别:LED是利用注入有源区的载流子自发辐射复合发光,而LD是受激辐射复合发光.在结构上的差别:LD有光学谐振腔,使产生的光子在腔内振荡放大,LED没有谐振腔.性能上 ...

  6. SDOI2018:原题识别

    题解: https://files.cnblogs.com/files/clrs97/old-solution.pdf Code: #include<cstdio> #include< ...

  7. Yii2 验证规则

    验证器的使用方法: public function rules() { return [ [['email', 'password'], 'required'], ['password', 'stri ...

  8. 前端性能优化 —— 添加Expires头

    简要:添加Expires头能有效的利用浏览器的缓存能力来改善页面的性能,能在后续的页面中有效避免很多不必要的Http请求,WEB服务器使用Expires头来告诉Web客户端它可以使用一个组件的当前副本 ...

  9. Array,prototype.concat.apply与[].conat.apply.

    一直都知道JS数组Array内置对象有一个concat方法,但是也没怎么研究过,今天偶然就看了看 concat是连接一个或多个数组 返回的是连接后数组的一个副本 var oldArr=[]; var ...

  10. GMA Round 1 新年的复数

    传送门 新年的复数 已知$\left\{\begin{matrix}A>B>0\\ AB=1\\ (A+B)(A-B)=2\sqrt{3}\end{matrix}\right.$ 求$(A ...