Java Lambda 表達式是Java 8 引入的一個新的功能,可以說是模擬函數式程式設計的一個語法糖,類似於Javascript 中的閉包,但又有些不同,主要目的是提供一個函數化的語法來簡化我們的編碼。
Lambda 基本語法
Lambda 的基本結構為(arguments) -> body,有以下幾種情況:
參數類型可推導時,不需要指定類型,如(a) -> System.out.println(參數類型可推導時,不需要指定類型,如(a) -> System.out.println( a)
當只有一個參數且類型可推導時,不強制寫(), 如a -> System.out.println(a)
參數指定類型時,必須有括號,如(int a) -> System.out.println(a)
參數可以為空,如() -> System.out.println(“hello”)
body 需要用{} 包含語句,當只有一條語句時{} 可省略
常見的寫法如下:
(a) -> a * a
(int a, int b) -> a + b
(a, b) -> {return a - b;}
() -> System.out.println(Thread.currentThread().getId())
函數式介面FunctionalInterface
概念
Java Lambda 表達式以函數式介面為基礎。什麼是函數式介面(FunctionalInterface)? 簡單說來就是只有一個方法(函數)的接口,這類接口的目的是為了一個單一的操作,也就相當於一個單一的函數了。常見的介面如:Runnable, Comparator 都是函數式接口,並且都標註了註解 @FunctionalInterface 。
舉例
以 Thread 為例說明很容易理解。 Runnable 介面是我們執行緒程式設計時常用的一個接口,就包含一個方法 void run(),這個方法就是執行緒的運行邏輯。按照以前的語法,我們新建線程一般要用到Runnable 的匿名類,如下:
new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getId()); } }).start();
如果寫多了,是不是很無聊,而基於Lambda 的寫法則變得簡潔明了,如下:
new Thread(() -> System.out.println(Thread.currentThread().getId())).start();
注意Thread 的參數,Runnable 的匿名實作就透過一句就實現了出來,寫成下面的更好理解
Runnable r = () -> System.out.println(Thread.currentThread().getId());
new Thread(r).start();
當然Lambda 的目的不僅僅是寫起來簡潔,更高層次的目的等體會到了再總結。
再看一個比較器的例子,依照傳統的寫法,如下:
Integer[] a = {1, 8, 3, 9, 2, 0, 5}; Arrays.sort(a, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2; } });
Lambda 表達式寫法如下:
Integer[] a = {1, 8, 3, 9, 2, 0, 5} ;
Arrays.sort(a, (o1, o2) -> o1 - o2);
JDK中的函數式介面
為了現有的類別庫能夠直接使用Lambda 表達式,Java 8 以前存在一些介面已標註為函數式介面的:
java.lang.Runnable
java.util.Comparator
java.util.concurrent.Callable
java.io.FileFilterpid🜵
. beans.PropertyChangeListenerJava 8 中更是新增加了一個包java.util.function,帶來了常用的函數式介面:
另外還對基本類型的處理增加了更具體的函數是接口,包括: BooleanSupplier, DoubleBinaryOperator, DoubleConsumer, DoubleFunction
Function
编译期间,我理解的类型推导的过程如下:
先确定目标类型 Function
Function 作为函数式接口,其方法签名为:Integer apply(String t)
检测 str -> Integer.parseInt(str) 是否与方法签名匹配(方法的参数类型、个数、顺序 和返回值类型)
如果不匹配,则报编译错误
这里的目标类型是关键,通过目标类型获取方法签名,然后和 Lambda 表达式做出对比。
方法引用
方法引用(Method Reference)的基础同样是函数式接口,可以直接作为函数式接口的实现,与 Lambda 表达式有相同的作用,同样依赖于类型推导。方法引用可以看作是只调用一个方法的 Lambda 表达式的简化。
方法引用的语法为: Type::methodName 或者 instanceName::methodName , 构造函数对应的 methodName 为 new。
例如上面曾用到例子:
Function
对应的方法引用的写法为
Function
根据方法的类型,方法引用主要分为一下几种类型,构造方法引用、静态方法引用、实例上实例方法引用、类型上实例方法引用等
构造方法引用
语法为: Type::new 。 如下面的函数为了将字符串转为数组
方法引用写法
Function
Lambda 写法
Function
传统写法
Function<String, Integer> strToInt = new Function<String, Integer>() { @Override public Integer apply(String str) { return new Integer(str); } };
数组构造方法引用
语法为: Type[]::new 。如下面的函数为了构造一个指定长度的字符串数组
方法引用写法
Function
方法引用写法
Function
传统写法
Function<Integer, String[]> fixedArray = new Function<Integer, String[]>() { @Override public String[] apply(Integer length) { return new String[length]; } };
静态方法引用
语法为: Type::new 。 如下面的函数同样为了将字符串转为数组
方法引用写法
Function
Lambda 写法
Function
传统写法
Function<String, Integer> strToInt = new Function<String, Integer>() { @Override public Integer apply(String str) { return Integer.parseInt(str); } };
实例上实例方法引用
语法为: instanceName::methodName 。如下面的判断函数用来判断给定的姓名是否在列表中存在
List
Predicate
System.out.println(checkNameExists.test("张三"));
System.out.println(checkNameExists.test("张四"));
类型上实例方法引用
语法为: Type::methodName 。运行时引用是指上下文中的对象,如下面的函数来返回字符串的长度
Function<String, Integer> calcStrLength = String::length; System.out.println(calcStrLength.apply("张三")); List<String> names = Arrays.asList(new String[]{"zhangsan", "lisi", "wangwu"}); names.stream().map(String::length).forEach(System.out::println);<br>
又比如下面的函数已指定的分隔符分割字符串为数组
BiFunction
String[] names = split.apply("zhangsan,lisi,wangwu", ",");
System.out.println(Arrays.toString(names));
Stream 对象
概念
什么是 Stream ? 这里的 Stream 不同于 io 中的 InputStream 和 OutputStream,Stream 位于包 java.util.stream 中, 也是 java 8 新加入的,Stream 只的是一组支持串行并行聚合操作的元素,可以理解为集合或者迭代器的增强版。什么是聚合操作?简单举例来说常见的有平均值、最大值、最小值、总和、排序、过滤等。
Stream 的几个特征:
单次处理。一次处理结束后,当前Stream就关闭了。
支持并行操作
常见的获取 Stream 的方式
从集合中获取
Collection.stream();
Collection.parallelStream();
静态工厂
Arrays.stream(array)
Stream.of(T …)
IntStream.range()
这里只对 Stream 做简单的介绍,下面会有具体的应用。要说 Stream 与 Lambda 表达式有什么关系,其实并没有什么特别紧密的关系,只是 Lambda 表达式极大的方便了 Stream 的使用。如果没有 Lambda 表达式,使用 Stream 的过程中会产生大量的匿名类,非常别扭。
举例
以下的demo依赖于 Employee 对象,以及由 Employee 对象组成的 List 对象。
public class Employee { private String name; private String sex; private int age; public Employee(String name, String sex, int age) { super(); this.name = name; this.sex = sex; this.age = age; } public String getName() { return name; } public String getSex() { return sex; } public int getAge() { return age; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Employee {name=").append(name).append(", sex=").append(sex).append(", age=").append(age) .append("}"); return builder.toString(); } } List<Employee> employees = new ArrayList<>(); employees.add(new Employee("张三", "男", 25)); employees.add(new Employee("李四", "女", 24)); employees.add(new Employee("王五", "女", 23)); employees.add(new Employee("周六", "男", 22)); employees.add(new Employee("孙七", "女", 21)); employees.add(new Employee("刘八", "男", 20));
打印所有员工
Collection 提供了 forEach 方法,供我们逐个操作单个对象。
employees.forEach(e -> System.out.println(e));
或者
employees.stream().forEach(e -> System.out.println(e));
按年龄排序
Collections.sort(employees, (e1, e2) -> e1.getAge() - e2.getAge());
employees.forEach(e -> System.out.println(e));
或者
employees.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).forEach(e -> System.out.println(e));
打印年龄最大的女员工
max/min 返回指定排序条件下最大/最小的元素
Employee maxAgeFemaleEmployee = employees.stream() .filter(e -> "女".equals(e.getSex())) .max((e1, e2) -> e1.getAge() - e2.getAge()) .get(); System.out.println(maxAgeFemaleEmployee);
打印出年龄大于20 的男员工
filter 可以过滤出符合条件的元素
employees.stream()
.filter(e -> e.getAge() > 20 && "男".equals(e.getSex()))
.forEach(e -> System.out.println(e));
打印出年龄最大的2名男员工
limit 方法截取有限的元素
employees.stream() .filter(e -> "男".equals(e.getSex())) .sorted((e1, e2) -> e2.getAge() - e1.getAge()) .limit(2) .forEach(e -> System.out.println(e));
打印出所有男员工的姓名,使用 , 分隔
map 将 Stream 中所有元素的执行给定的函数后返回值组成新的 Stream
String maleEmployeesNames = employees.stream() .map(e -> e.getName()) .collect(Collectors.joining(",")); System.out.println(maleEmployeesNames);
统计信息
IntSummaryStatistics, DoubleSummaryStatistics, LongSummaryStatistics 包含了 Stream 中的汇总数据。
IntSummaryStatistics stat = employees.stream() .mapToInt(Employee::getAge).summaryStatistics(); System.out.println("员工总数:" + stat.getCount()); System.out.println("最高年龄:" + stat.getMax()); System.out.println("最小年龄:" + stat.getMin()); System.out.println("平均年龄:" + stat.getAverage());
总结
Lambda 表达式确实可以减少很多代码,能提高生产力,当然也有弊端,就是复杂的表达式可读性会比较差,也可能是还不是很习惯的缘故吧,如果习惯了,相信会喜欢上的。凡事都有两面性,就看我们如何去平衡这其中的利弊了,尤其是在一个团队中。
以上就是对Java8 JavaLambda 的资料整理,后续继续补充相关资料谢谢大家对本站的支持!
更多Java Lambda 表达式详解及示例代码相关文章请关注PHP中文网!

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA

本文解釋了用於構建分佈式應用程序的Java的遠程方法調用(RMI)。 它詳細介紹了接口定義,實現,註冊表設置和客戶端調用,以解決網絡問題和安全性等挑戰。

本文詳細介紹了用於網絡通信的Java的套接字API,涵蓋了客戶服務器設置,數據處理和關鍵考慮因素,例如資源管理,錯誤處理和安全性。 它還探索了性能優化技術,我

本文詳細介紹了創建自定義Java網絡協議。 它涵蓋協議定義(數據結構,框架,錯誤處理,版本控制),實現(使用插座),數據序列化和最佳實踐(效率,安全性,維護


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

Atom編輯器mac版下載
最受歡迎的的開源編輯器

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 Linux新版
SublimeText3 Linux最新版

SublimeText3漢化版
中文版,非常好用