Java中的函数式编程(三)lambda表达式
写在前面
lambda表达式与匿名内部类
无参的函数式接口
public static void createThreadWithAnonymousClass() {
// Runnable 是接口名。我们通过匿名内部类的方式,构造了一个 Runnable 的实例。
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running");
}
});
t.start();
}
使用匿名内部类的一个重要目的,就是为了减轻程序员的代码负担,不需要额外再定义一个类,而且这个类是一个一次性的类,没有太多的重用价值。但是,我们会发现,这个对象看起来也是多余的,因为我们实际上并不是要传入一个对象,而只是想传入一个方法。
public static void createThreadWithLambda() {
// 在Java 8中,Runnable 是一个函数式接口,因此我们可以使用 lambda 表达式来实现它。
Thread t = new Thread(() -> {
System.out.println("Thread is running");
});
t.start();
}
带参的函数式接口
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2); ....
}
假设一个场景:给定一个省份的拼音列表,需要对该列表中的省份进行排序,排序规则是字母长度最小的省份排在前面,如果两个省份字母长度一样,则按字母顺序排序。
public static void sortProvincesWithAnonymousClass() {
List list = Arrays.asList("Guangdong", "Zhejiang", "Jiangsu", "Xizang", "Fujian", "Hunan", "Guangxi");
list.sort(new Comparator<String>() {
@Override
public int compare(String first, String second) {
int lenDiff = first.length() - second.length();
return lenDiff == 0 ? first.compareTo(second) : lenDiff;
}
});
list.forEach(s -> System.out.println(s));
}
上述代码输出为:
public static void sortProvincesWithLambda() {
List list = Arrays.asList("Guangdong", "Zhejiang", "Jiangsu", "Xizang", "Fujian", "Hunan", "Guangxi");
// 下面的参数列表 first 和 second ,即方法 Comparator.compare 的参数列表
list.sort((first, second) -> {
int lenDiff = first.length() - second.length();
return lenDiff == 0 ? first.compareTo(second) : lenDiff;
});
list.forEach(s -> System.out.println(s));
}
注意到,带参数的lambda表达式,甚至不需要声明类型,因为编译器可以通过上下文来推断出参数的类型。当然,我们也可以显式指定参数类型,尤其是在参数类型推断失败的时候:
(String first, String second) -> {
int lenDiff = first.length() - second.length();
return lenDiff == 0 ? first.compareTo(second) : lenDiff;
}
this关键字的作用域
public class ThisScopeExample {
public static void main(String[] args) {
ThisScopeExample example = new ThisScopeExample();
// 输出 "I am Anonymous Class."
example.runWithAnonymousClass();
// 输出 "I am ThisScopeExample Class."
example.runWithLambda();
}
public void runWithAnonymousClass() {
// 以匿名类的方式运行
run(new Runnable() {
@Override
public void run() {
// this 是实现了接口 Runnable 的匿名内部类的实例
System.out.println(this);
}
@Override
public String toString() {
return "I am Anonymous Class.";
}
});
}
public void runWithLambda() {
// 以lambda表达式的方式运行
run(() -> {
// this 是类 ThisScopeExample 的实例
System.out.println(this);
});
}
public void run(Runnable runnable) {
runnable.run();
}
@Override
public String toString() {
return "I am ThisScopeExample Class.";
}
}
lambda表达式的语法
(String first, String second) -> {
int lenDiff = first.length() - second.length();
return lenDiff == 0 ? first.compareTo(second) : lenDiff;
}
上述是一个典型的而且完整的lambda表达式。
Supplier supplier = () -> {
return new Random().nextInt(100);
}
对于上面的lambda表达式,可以发现它的方法体只有一个表达式,所以,它可以省略大括号,甚至return关键字也省略了,因为编译器可以根据上下文推断是否需要返回值:如果需要,那么就返回该唯一表达式的返回值,如果不需要,则在该唯一表达式后直接return。例如:
// Supplier 是需要返回值的,所以下面的lambda表达式等同于:
// () -> { return new Random().nextInt(100); }
Supplier supplier = () -> new Random().nextInt(100); // Runnable 是不需要返回值的,所以下面的lambda表达式等同于:
// () -> { new Random().nextInt(100); return; }
Runnable runnable = () -> new Random().nextInt(100);
如果编译器可以推断出lambda表达式的参数类型,则可以忽略其类型:
// 在这里,编译器可以推断出 first 和 second 的类型是 String。
Comparator comp = (first, second) -> {
int lenDiff = first.length() - second.length();
return lenDiff == 0 ? first.compareTo(second) : lenDiff;
};
如果lambda表达式只有一个参数,那么参数列表中的小括号也可以省略掉:
// 这里的 value ,等同于 (value)
Consumer consumer = value -> System.out.println(value);
与普通的函数不一样,lambda表达式不需要指定返回类型,它总是由编译器自行推断出返回类型。如果推断失败,则默认为Object类型。
lambda表达式与闭包
public class ClosureExample {
public static void main(String[] args) {
// 平方
IntUnaryOperator square = getPowOperator(2);
// 立方
IntUnaryOperator cube = getPowOperator(3);
// 四次方
IntUnaryOperator fourthPower = getPowOperator(4);
// 5的平方
System.out.println(square.applyAsInt(5));
// 5的立方
System.out.println(cube.applyAsInt(5));
// 5的四次方
System.out.println(fourthPower.applyAsInt(5));
}
public static IntUnaryOperator getPowOperator(int exp) {
return base -> {
// 变量 exp 是 getPowOperator 的参数,属于lambda 表达式定义时的自由变量,
// 它的生命周期会延长到和返回的 lambda 表达式一样长。
return (int) Math.pow(base, exp);
};
}
}
上述代码的输出是:
public static IntUnaryOperator getPowOperator(int exp) {
// 尝试修改 exp 的值,但编译器会在lambda表达式中报错
exp++;
return base -> {
// 如果尝试修改 exp 的值,会在此处报错:
// Error: 从lambda 表达式引用的本地变量必须是final变量或实际上的final变量
return (int) Math.pow(base, exp);
};
}
但这种限制也是有限的,因为我们可以通过将变量声明为一个数组或一个类就可以修改其中的值。例如:
public static IntUnaryOperator getPowOperator(int[] exp) {
// exp 是一个int数组:exp = new int[1];
exp[0]++;
return base -> {
// 此时不会报错,可以正常运行
return (int) Math.pow(base, exp[0]);
};
}
结语
为方便大家在移动端浏览,已注册微信公众号【员说】,欢迎关注。第一时间更新技术文章,也会不定时分享圈内热门动态和一线大厂内幕。
感谢您阅读本篇文章,如果觉得本文对您有帮助,欢迎点击推荐和关注,您的支持是我最大的写作动力。
文章欢迎转载,但需在文章页面明显位置,给出作者和原文链接,否则保留追究法律责任的权利!
注意!应各位朋友的邀请,创建了一个技术交流群,(聊技术/看内幕/找内推/读书分享等,拒绝水群,保证品质),可添加微信号【yuanshuo824】,备注:交流,即可入群。
Java中的函数式编程(三)lambda表达式的更多相关文章
- Java 中的函数式编程(Functional Programming):Lambda 初识
Java 8 发布带来的一个主要特性就是对函数式编程的支持. 而 Lambda 表达式就是一个新的并且很重要的一个概念. 它提供了一个简单并且很简洁的编码方式. 首先从几个简单的 Lambda 表达式 ...
- Java 函数式编程(Lambda表达式)与Stream API
1 函数式编程 函数式编程(Functional Programming)是编程范式的一种.最常见的编程范式是命令式编程(Impera Programming),比如面向过程.面向对象编程都属于命令式 ...
- Java 函数式编程和Lambda表达式
1.Java 8最重要的新特性 Lambda表达式.接口改进(默认方法)和批数据处理. 2.函数式编程 本质上来说,编程关注两个维度:数据和数据上的操作. 面向对象的编程泛型强调让操作围绕数据,这样可 ...
- Java中的函数式编程(五)Java集合框架中的高阶函数
写在前面 随着Java 8引入了函数式接口和lambda表达式,Java 8中的集合框架(Java Collections Framework, JCF)也增加相应的接口以适应函数式编程. 本文的 ...
- Java中的函数式编程(六)流Stream基础
写在前面 如果说函数式接口和lambda表达式是Java中函数式编程的基石,那么stream就是在基石上的最富丽堂皇的大厦. 只有熟悉了stream,你才能说熟悉了Java 的函数式编程. 本文主要介 ...
- Java函数式编程和lambda表达式
为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于 ...
- 函数式编程--使用lambda表达式
前面一篇博客我们已经说到了,lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口的实例.现在我们来写一段java的命令者模式来自己研究下lambda表达式的语法. 这里重复下命令者模式: ...
- Java8函数式编程和lambda表达式
文章目录函数式编程JDK8接口新特性函数接口方法引用函数式编程函数式编程更多时候是一种编程的思维方式,是一种方法论.函数式与命令式编程区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉 ...
- Java8函数式编程以及Lambda表达式
第一章 认识Java8以及函数式编程 尽管距离Java8发布已经过去7.8年的时间,但时至今日仍然有许多公司.项目停留在Java7甚至更早的版本.即使已经开始使用Java8的项目,大多数程序员也仍然采 ...
随机推荐
- 在python3.6环境下使用cxfreeze打包程序
在python3.6环境下使用cxfreeze打包程序 环境:python3.6 打包程序:aliens_invasion 原本想使用pyintaller 进行打包,使用pip的安装过程也没有问题,打 ...
- uniapp 封装 request 并 配置跨域,( 本地 + 线上 + 封装 )
找到上面这个 文件,不管是用 命令创建 还是 用 HBX 创建,都一样会有这个文件的,然后跟着截图复制粘贴就好了. // 这是配置本地能跨域的,或者你可以直接让后端给你设置请求头,避免了跨域. &qu ...
- Java并发之锁升级:无锁->偏向锁->轻量级锁->重量级锁
Java并发之锁升级:无锁->偏向锁->轻量级锁->重量级锁 对象头markword 在lock_bits为01的大前提下,只有当是否偏向锁位值为1的时候,才表明当前对象处于偏向锁定 ...
- SQL语句之高级使用
1.select top select top 用于规定要返回的数据的数目 注意:并非所有的数据库系统都支持 SELECT TOP 语句. MySQL 支持 LIMIT 语句来选取指定的条数数据, ...
- vue 动态ip配置,避免重复打包
目前比较流行的打包大都是在vue.config.js配置代理,然后在根目录新建.env.xxx文件配置正式环境,测试环境,开发环境等用于打包时配置不同的访问地址,作为一名随波逐流的前端开发,我也是这么 ...
- 轻量化模型系列--GhostNet:廉价操作生成更多特征
前言 由于内存和计算资源有限,在嵌入式设备上部署卷积神经网络 (CNN) 很困难.特征图中的冗余是那些成功的 CNN 的一个重要特征,但在神经架构设计中很少被研究. 论文提出了一种新颖的 Gh ...
- 【第三篇】- Git 工作流程之Spring Cloud直播商城 b2b2c电子商务技术总结
Git 工作流程 本章节我们将为大家介绍 Git 的工作流程. 一般工作流程如下: 克隆 Git 资源作为工作目录. 在克隆的资源上添加或修改文件. 如果其他人修改了,你可以更新资源. 在提交前查看修 ...
- io流-文件流\节点流
FileOutputStream类(jdk1.0) 描述 java.io.FileOutputStream 类是文件字节输出流,用于将数据写入到文件中. 构造方法 //构造方法 FileOutputS ...
- 解决git bash闪退问题 报openssl错误
问题描述:今天安装git之后发现Git Bash工具闪退. 于是试了各种办法之后,最后终于解决. 背景描述:git 下载地址:https://git-scm.com/download/win 下载成功 ...
- 机器学习——最优化问题:拉格朗日乘子法、KKT条件以及对偶问题
1 前言 拉格朗日乘子法(Lagrange Multiplier) 和 KKT(Karush-Kuhn-Tucker) 条件是求解约束优化问题的重要方法,在有等式约束时使用拉格朗日乘子法,在有不等 ...