Java8新特性探索之Lambda表达式
为什么引入Lambda表达式?
- Lambda 表达式产生函数,而不是类。 在 JVM(Java Virtual Machine,Java 虚拟机)上,一切都是一个类,因此在幕后执行各种操作使 lambda 看起来像函数 —— 但作为程序员,你可以高兴地假装它们“只是函数”。
- Lambda 语法尽可能少,使用 Lambda 表达式可以使代码变的更加简洁紧凑, 易于编写和使用。
- Lambda 表达式通常比匿名内部类产生更易读的代码。
如何使用Lambda表达式?
首先我们先了解下Lambda表达式的重要特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
任何 Lambda 表达式的基本语法是:
- 参数
- 接着
->,可视为“产出”。 ->之后的内容都是方法体。- [1] 当只用一个参数,可以不需要括号
()。 然而,这是一个特例。 - [2] 正常情况使用括号
()包裹参数。 为了保持一致性,也可以使用括号()包裹单个参数,虽然这种情况并不常见。 - [3] 如果没有参数,则必须使用括号
()表示空参数列表。 - [4] 对于多个参数,将参数列表放在括号
()中。 - [5] 如果在 Lambda 表达式中确实需要多行,则必须将这些行放在花括号中。 在这种情况下,就需要使用 return。到目前为止,所有 Lambda 表达式方法体都是单行。 该表达式的结果自动成为 lambda 表达式的返回值,在此处使用 return 关键字是非法的。 这是 lambda 表达式简化相应语法的另一种方式。
- [1] 当只用一个参数,可以不需要括号
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 表达式规约
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 // 不合理,须显示声明所有参数类型
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;
};
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()); //编译会出错
总结
- Lambda表达式没有命名,用来像传递数据一样传递操作。
- 函数接口指的是只有一个抽象方法的接口,被当做是lambda表达式的类型。
Java8新特性探索之Lambda表达式的更多相关文章
- Java8新特性之一:Lambda表达式
Java8是自java5之后最重大的一次更新,它给JAVA语言带来了很多新的特性(包括编译器.类库.工具类.JVM等),其中最重要的升级是它给我们带来了Lambda表达式和Stream API. 1. ...
- Java8新特性之forEach+Lambda 表达式遍历Map和List
这是Java8系列的第二篇,今天来说一下Java8中forEach的简单使用.我们使用对比的方式来看应该会看得更加清楚,更能理解: 一.遍历Map ============Java8之前的方式==== ...
- java8新特性1:lambda表达式和函数式接口
1.lambda的介绍: 1.1.为什么java语言需要引入lambda表达式? java语言诞生于1995年,历史时间已经相对较长了.在其后的各种新型编程语言中,都有着lambda表达式的内容,并且 ...
- Java8新特性(1):Lambda表达式
Lambda表达式可以理解为一种匿名函数:没有名称,但有参数列表.函数主体.返回类型.它是行为参数化的一种实现,行为参数化是指将不同的行为作为参数传递给方法,方法的所具备的能力取决于它接收的行为参数. ...
- java8新特性:内存和lambda表达式
1.内存变化 取消了永久区和方法区,取而代之的是MetaSpace元空间,即直接使用物理内存,即电脑内存8G则直接使用8g内存,而不是分配内存.因为内存改变,所以调整性能对应的调整参数也随之改变. 2 ...
- Java8新特性(拉姆达表达式lambda)
一.函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是只包含一个方法的接口.比如Java标准库中的java.lang.Runn ...
- java8 新特性入门 stream/lambda
Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利.高效的聚合操作(aggregate operation),或者大批量数据操作 (b ...
- 乐字节-Java8核心特性实战之Lambda表达式
大家好,小乐又来给大家分享Java8核心特性了,上一篇文章是<乐字节|Java8核心实战-接口默认方法>,这次就来讲Java8核心特征之Lambda表达式. Java8 引入Lambda表 ...
- Java8新特性探索之Stream接口
一.为什么引入Stream流 流是一系列与特定存储机制无关的元素--实际上,流并没有"存储"之说.使用流,无需迭代集合中的元素,就可以从管道提取和操作元素.这些管道通常被组合在一起 ...
随机推荐
- IDEA环境配置里的一些error
1. idea添加edit configuration 没有tomcat图标 File->Setting->Build,Execution,Deployment-->Applicat ...
- Apollo系列(一):分布式配置中心Apollo安装(Linux、Docker)
一.介绍 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管理 ...
- 干货满满!关于Pycharm远程开发
可以在Windows中使用Pycharm编写代码,而代码的调试运行可以使用远程服务器中的python解释器. 在本地创建好工程项目(或从git上clone下代码)后,用Pycharm打开: 打开「To ...
- Centos-搜索文件或目录-find
find 在指定的目录下查找指定的文件 相关选项 -type 指定文件类型 -name 指定文件名字,支持通配符 -gid 指定用户组ID -uid 指定用户ID -empty 查找长度为 ...
- Centos-切换用户身份-su
su 切换用户身份 相关选项 - 加载相应用户下环境变量 -c 使用某个身份执行一个指令 -m 改变用户身份不改变环境变量 切换为超级用户 su - 普通用户切换为超级用户需要输入密码,超级用户 ...
- Python练习题 034:Project Euler 006:和平方与平方和之差
本题来自 Project Euler 第6题:https://projecteuler.net/problem=6 # Project Euler: Problem 6: Sum square dif ...
- JS之回调函数(callback)
1.什么是回调函数? -- 简单点说,一个函数被作为参数传递给另一个函数(在这里我们把另一个函数叫做"otherFunction"),回调函数在otherFunction中被调用. ...
- Spring Boot第五弹,WEB开发初了解~
持续原创输出,点击上方蓝字关注我吧 目录 前言 Spring Boot 版本 前提条件(必须注意) 添加依赖 第一个接口开发 如何自定义tomcat的端口? 如何自定义项目路径? JSON格式化 日期 ...
- 《C++ primer plus》第3章练习题
注:有的题设条件自己改动了一下,比如英寸英尺改成米和厘米,部分题目自己加了点额外要求. 1.要求用户输入身高(m),用下划线提示用户输入,将身高转化成"米"加"厘米&qu ...
- 【题解】Tree
题目戳我 \(\text{Solution:}\) 考虑点分治.对于这个两点之间,它意味着这点对必须是不一样的. 考虑用双指针统计答案.显然,对于两个数\(a,b\),要让\(a+b=k,a\)越大则 ...