为什么引入Lambda表达式?

  • Lambda 表达式产生函数,而不是类。 在 JVM(Java Virtual Machine,Java 虚拟机)上,一切都是一个类,因此在幕后执行各种操作使 lambda 看起来像函数 —— 但作为程序员,你可以高兴地假装它们“只是函数”。
  • Lambda 语法尽可能少,使用 Lambda 表达式可以使代码变的更加简洁紧凑, 易于编写和使用。
  • Lambda 表达式通常比匿名内部类产生更易读的代码。

如何使用Lambda表达式?

首先我们先了解下Lambda表达式的重要特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

任何 Lambda 表达式的基本语法是:

  1. 参数
  2. 接着 ->,可视为“产出”。
  3. -> 之后的内容都是方法体。
    • [1] 当只用一个参数,可以不需要括号 ()。 然而,这是一个特例。
    • [2] 正常情况使用括号 () 包裹参数。 为了保持一致性,也可以使用括号 () 包裹单个参数,虽然这种情况并不常见。
    • [3] 如果没有参数,则必须使用括号 () 表示空参数列表。
    • [4] 对于多个参数,将参数列表放在括号 () 中。
    • [5] 如果在 Lambda 表达式中确实需要多行,则必须将这些行放在花括号中。 在这种情况下,就需要使用 return。到目前为止,所有 Lambda 表达式方法体都是单行。 该表达式的结果自动成为 lambda 表达式的返回值,在此处使用 return 关键字是非法的。 这是 lambda 表达式简化相应语法的另一种方式。

Lambda 表达式实例

// 1. 不需要参数,返回值为 5
() -> 5 // 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x // 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y // 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

我们在 LambdaExpressions.java 中看到了一个 Lambda 表达式,但还有其他语法变体:

interface Description {
String brief();
} interface Body {
String detailed(String head);
} interface Multi {
String twoArg(String head, Double d);
} public class LambdaExpressions { static Body bod = h -> h + " No Parens!"; // [1] static Body bod2 = (h) -> h + " More details"; // [2] static Description desc = () -> "Short info"; // [3] static Multi mult = (h, n) -> h + n; // [4] static Description moreLines = () -> { // [5]
System.out.println("moreLines()");
return "from moreLines()";
}; public static void main(String[] args) {
System.out.println(bod.detailed("Oh!"));
System.out.println(bod2.detailed("Hi!"));
System.out.println(desc.brief());
System.out.println(mult.twoArg("Pi! ", 3.14159));
System.out.println(moreLines.brief());
}
}

输出结果:

Oh! No Parens!
Hi! More details
Short info
Pi! 3.14159
moreLines()
from moreLines()

lambda 表达式规约

  1. Lambda 表达式的参数可以通过上下文推断,如果需要显示声明一个参数的类型,则必须为所有的参数声明类型。

    @FunctionalInterface
    public interface IFunctionMod {
    boolean (int n, int d);
    } IFunctionMod function = (n, d) -> (n % d) == 0 // 合理,n 和 d 的类型通过上下文推断
    IFunctionMod function = (int n, int d) -> (n % d) == 0 // 合理,指定 n 和 d 的类型
    IFunctionMod function = (int n, d) -> (n % d) == 0 // 不合理,须显示声明所有参数类型
  2. Lambda 表达式中抛出的异常需要与目标函数式接口的抽象方法抛出的异常类型兼容。

    以下是合理的:

    @FunctionalInterface
    public interface IFunctionMod {
    boolean (int n, int d) throw Exception;
    } IFunctionMod function = (n, d) -> {
    if (d == 0) {
    // IOException是EXception 的子类,通过类型转换,IOException 可转换为 Exception
    throw new IOException("test");
    }
    return n % d == 0;
    };

    如果反一下,就不行:

    @FunctionalInterface
    public interface IFunctionMod {
    boolean (int n, int d) throw IOException;
    } IFunctionMod function = (n, d) -> {
    if (d == 0) {
    // 父类不能通过自动类型转换转为子类,lambda 表达式抛出的异常类型与抽象方法抛出的异常类型不兼容
    throw new Exception("test");
    }
    return n % d == 0;
    };
  3. Lambda 表达式中参数类型需要与目标函数式接口中抽象方法的参数类型兼容。从接口与实现的角度,可以很容易理解抛出异常兼容 和 参数类型兼容 这2点。

变量作用域

lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。

  • lambda 表达式中访问外层的局部变量:

    public class LambdaTester {
    public static void main(String args[]) {
    final int num = 1;
    Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
    s.convert(2); // 输出结果为 3
    } public interface Converter<T1, T2> {
    void convert(int i);
    }
    }
  • lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)

    int num = 1;
    Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
    s.convert(2);
    num = 5;
    //报错信息:Local variable num defined in an enclosing scope must be final or effectively
    final
  • lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

    String first = "";
    Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错

总结

  1. Lambda表达式没有命名,用来像传递数据一样传递操作。
  2. 函数接口指的是只有一个抽象方法的接口,被当做是lambda表达式的类型。

Java8新特性探索之Lambda表达式的更多相关文章

  1. Java8新特性之一:Lambda表达式

    Java8是自java5之后最重大的一次更新,它给JAVA语言带来了很多新的特性(包括编译器.类库.工具类.JVM等),其中最重要的升级是它给我们带来了Lambda表达式和Stream API. 1. ...

  2. Java8新特性之forEach+Lambda 表达式遍历Map和List

    这是Java8系列的第二篇,今天来说一下Java8中forEach的简单使用.我们使用对比的方式来看应该会看得更加清楚,更能理解: 一.遍历Map ============Java8之前的方式==== ...

  3. java8新特性1:lambda表达式和函数式接口

    1.lambda的介绍: 1.1.为什么java语言需要引入lambda表达式? java语言诞生于1995年,历史时间已经相对较长了.在其后的各种新型编程语言中,都有着lambda表达式的内容,并且 ...

  4. Java8新特性(1):Lambda表达式

    Lambda表达式可以理解为一种匿名函数:没有名称,但有参数列表.函数主体.返回类型.它是行为参数化的一种实现,行为参数化是指将不同的行为作为参数传递给方法,方法的所具备的能力取决于它接收的行为参数. ...

  5. java8新特性:内存和lambda表达式

    1.内存变化 取消了永久区和方法区,取而代之的是MetaSpace元空间,即直接使用物理内存,即电脑内存8G则直接使用8g内存,而不是分配内存.因为内存改变,所以调整性能对应的调整参数也随之改变. 2 ...

  6. Java8新特性(拉姆达表达式lambda)

    一.函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是只包含一个方法的接口.比如Java标准库中的java.lang.Runn ...

  7. java8 新特性入门 stream/lambda

    Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利.高效的聚合操作(aggregate operation),或者大批量数据操作 (b ...

  8. 乐字节-Java8核心特性实战之Lambda表达式

    大家好,小乐又来给大家分享Java8核心特性了,上一篇文章是<乐字节|Java8核心实战-接口默认方法>,这次就来讲Java8核心特征之Lambda表达式. Java8 引入Lambda表 ...

  9. Java8新特性探索之Stream接口

    一.为什么引入Stream流 流是一系列与特定存储机制无关的元素--实际上,流并没有"存储"之说.使用流,无需迭代集合中的元素,就可以从管道提取和操作元素.这些管道通常被组合在一起 ...

随机推荐

  1. xxe漏洞,及xml

    xxe漏洞 XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言.XML文档结构包括XML声明.DTD文档类型定义(可选).文 ...

  2. Logback自定义日志颜色

    片段 1 片段 2 LogbackColorful.java package cn.mrxionge.netdemo; import ch.qos.logback.classic.Level; imp ...

  3. JAVA简单上传图片至七牛

    utils package com.example.demo.utils; import com.alibaba.fastjson.JSONObject; import com.qiniu.commo ...

  4. @ComponentScan比较

    ComponetScan 定义扫描规则 value:指定要扫描的包 excludeFilters=Filter[] 指定扫描的时候按照什么规则排除哪些组件. includeFilters=Filter ...

  5. 一加云耳2和一加云耳z区别

    [解码方式]:云耳Z采用的音源解码方式是AAC,云耳2使用的是APTX: [发声单元]:云耳Z采用的是动圈,云耳2采用的是动圈+动铁组合 [颜色]:云耳Z有4款颜色(宝蓝.薄荷绿.黑色.米白)云耳2有 ...

  6. 腾讯云Elasticsearch集群规划及性能优化实践

    ​一.引言 随着腾讯云 Elasticsearch 云产品功能越来越丰富,ES 用户越来越多,云上的集群规模也越来越大.我们在日常运维工作中也经常会遇到一些由于前期集群规划不到位,导致后期业务增长集群 ...

  7. 阿里云oss对象存储配置CDN

    阿里云oss对象存储配置CDN 1.打开阿里云CDN 2.填写信息,这个地方要注意,我的备案域名是www.ljwXXX.work,我们可以自定义一个域名,test.ljwXXX.work作为加速域名. ...

  8. Java知识系统回顾整理01基础03变量02基本变量类型

    一.变量类型分类 一个变量的类型,决定了该变量可以包含什么样的值. Java中有八种基本类型,都是Java语言预先定义好的,并且是关键字. 这八种基本类型分别是:  整型 (4种) 字符型 (1种) ...

  9. Java知识系统回顾整理01基础06数组04增强型for循环

    增强型for循环在遍历一个数组的时候会更加快捷 一.增强型for循环 注:增强型for循环只能用来取值,却不能用来修改数组里的值 public class HelloWorld { public st ...

  10. 【题解】[SDOI2015]星际战争

    \(\color{red}{Link}\) \(\text{Solution:}\) 观察到,如果一个时间\(T\)可以完成任务,则\(T+1\)这个时间也可以完成任务. 于是我们可以二分. 为了避免 ...