java8新特性-lambda表達(dá)式入門學(xué)習(xí)心得
定義
jdk8發(fā)布新特性中,lambda是一大亮點之一。lambda表達(dá)式能夠簡化我們對數(shù)據(jù)的操作,減少代碼量,大大提升我們的開發(fā)效率。
Lambda 表達(dá)式”(lambda expression)是一個匿名函數(shù),Lambda表達(dá)式基于數(shù)學(xué)中的λ演算得名,直接對應(yīng)于其中的lambda抽象(lambda abstraction),是一個匿名函數(shù),即沒有函數(shù)名的函數(shù)。
Lambda表達(dá)式可以表示閉包。如果你之前了解scala和js函數(shù)式編程,將會更加快速上手和學(xué)習(xí)java8的lambda新特性。
lambda表達(dá)式的語法
Lambda表達(dá)式在Java8中引入了一個新的語法操作符號,即:->
,它將Lambda表達(dá)式分為兩部分。
左側(cè)
- Lambda表達(dá)式左側(cè)為入?yún)?shù)。
右側(cè)
- Lambada表示式的右側(cè)表示執(zhí)行的功能。
總結(jié)就是:
(parameters) -> expression 或 (parameters) ->{ statements; }
以下是Lambda表達(dá)式幾種語法格式:
1.無參,無返回值,典型的一個例子是Runnable匿名內(nèi)部類的使用。
// java 8之前的寫法 Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " >---Lambda"); } }; // 使用lambda的寫法 Runnable r = () -> System.out.println(Thread.currentThread().getName() + " >---Lambda");
2.一個參數(shù)的使用
// java 8 之前的寫法 Consumer<String> consumer = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; // Lambda表達(dá)式的寫法 Consumer<String> consumer = (par)->System.out.println(par); consumer.accept("xixi"); 一個參數(shù)的小括號可以省略,簡化如下: Consumer<String> consumer = par->System.out.println(par);
3.兩個參數(shù)的使用
// java 8之前的寫法 Comparator<Integer> comparator = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }; // 使用Lambda表達(dá)式的方法 ,當(dāng)只有一條語句的時候,大括號和return都可以省略 Comparator<Integer> comparator=(x,y) -> x.compareTo(y);
觀察上面的代碼后,我們發(fā)現(xiàn)使用Lambda表達(dá)式,在Lambda表達(dá)式中并沒有指定入?yún)⒌膮?shù)類型。這個編譯和運行沒有報錯,這個是怎么判斷出來的呢?
很簡單是類型推斷的作用,java8中有個很大的變化,就是類型推斷,簡單來說javac在編譯代碼時候,會根據(jù)程序的上下文來推斷出Lambda表達(dá)式參數(shù)類型。
例如上文中的下面這個代碼:
Comparator<Integer> comparator=(x,y) -> x.compareTo(y);
這里在編譯的時候,在執(zhí)行x.compareTo(y)
的時候根據(jù)類型推斷,因為這個接口定義數(shù)據(jù)的泛型是Intger,所以根據(jù)類型推斷會自動調(diào)用Integer.compareTo方法。
為理解lambda表達(dá)式的作用,以及簡化我們開發(fā)。這兒將會舉個小小的例子。
需求
王小明所在的公司,每一個月都會進(jìn)行薪資財務(wù)報表,王小明每一個月都會自己對員工們的薪資做統(tǒng)計,以了解公司財務(wù)支出和訂單提成等需求。常常有做訂單提成排名和總工資排名的這樣的一個需求。
我們將定義以下類,來完成王小明的統(tǒng)計需求。
基本員工類
package com.codegeek.lambda; import lombok.*; @Setter @Getter @NoArgsConstructor @ToString public class Employee { /** * 員工姓名 */ private String name; /** * 員工年齡 */ private int age; /** * 基本薪水 */ private double basicSalary; /** * 訂單成交總額 */ private double dealTotalPrice; public Employee(String name, int age, double basicSalary,double dealTotalPrice) { this.name = name; this.age = age; this.basicSalary = basicSalary; this.dealTotalPrice = dealTotalPrice; } /** * 員工總薪資 * * @return Double */ public Double getTotalSalary() { return this.basicSalary + this.dealTotalPrice * 0.04; } }
現(xiàn)在假設(shè)在A部門有,青龍,白虎,朱雀,玄武 四個部門人員。下面是他們上個月基本薪資的情況。
Employee qingLong = new Employee("青龍", 25, 5500, 7500); Employee baiHu = new Employee("白虎", 27, 5000, 9000); Employee zhuQue = new Employee("朱雀", 22, 3800, 4500); Employee xuanWu = new Employee("玄武", 24, 3300, 3300); List<Employee> employees = Arrays.asList(qingLong, baiHu, zhuQue, xuanWu);
現(xiàn)在有個統(tǒng)計的需求是,按員工年齡從小到大排列,并獲取員工姓名列表。讓我們分別使用Lambda表達(dá)式和java8之前的做法。
java8之前通常的做法
// 員工列表先進(jìn)行排序 employees.sort(new Comparator<Employee>() { @Override public int compare(Employee o1, Employee o2) { Integer age1 = o1.getAge(); Integer age2 = o2.getAge(); return age1.compareTo(age2); } }); // 遍歷排序后的列表并輸出員工姓名 for (Employee employee : employees) { System.out.println(employee.getName()); }
使用Lambda的做法
employees.stream().sorted((o1, o2) -> o1.getAge().compareTo(o2.getAge())) .forEach(o -> System.out.println(o.getName()));
看到這里我們一定知道Lambda表達(dá)式使用的方便,確實減少了很多代碼的使用。
函數(shù)式接口
只包含一個抽象方法的接口,稱為函數(shù)式接口。
使用Lambda表達(dá)式創(chuàng)建該對象接口的對象,如果Lambda拋出一個受檢異常,通常需要在目標(biāo)接口使用@FunctionalInterface
注解,來聲明標(biāo)記了該注解的接口是一個函數(shù)式接口。
例如:
Consumer<String> consumer = par->System.out.println(par);
就是一個典型的消費型函數(shù)式接口。
注意觀察該接口的源代碼,只包含一個抽象的方法的接口是函數(shù)式接口,下面andThen
是一個默認(rèn)方法,并不屬于抽象方法。不要被迷惑了。
內(nèi)建函數(shù)式的接口
jdk8中默認(rèn)定義了很多函數(shù)式接口,主要使用的有下面四個。
函數(shù)式接口 | 參數(shù)類型 | 返回類型 | 使用說明 |
---|---|---|---|
Consumer<T> 消費型接口 | T | void | 對類型T的數(shù)據(jù)進(jìn)行操作,抽象方法 void accept(T t) |
Supplier 供給型接口 | 無 | T | 返回類型T的對象,抽象方法 T get(); |
Function<T,R> 函數(shù)型接口 | T | R | 對類型T對象進(jìn)行操作,返回類型R的對象,抽象方法R apply(T t) |
Predicate<T> 斷言型接口 | T | bolean | 對類型T對象進(jìn)行操作,返回類型boolean,抽象方法boolean test(T t) |
四大函數(shù)式接口的使用
public class FourFunctionsTest { // 消費式接口 @Test public void testConsumer() { Consumer<Integer> consumer = x -> System.out.println(x); consumer.accept(1); } // 供給式接口 @Test public void testSupplier() { Supplier<String> supplier = () -> { StringBuffer sb = new StringBuffer(); return sb.append("我").append(520).append("you").toString(); }; System.out.println(supplier.get()); } // 斷言式接口 @Test public void testPredicate() { Predicate<Long> predicate = x -> x == 1L; System.out.println(predicate.test(2L)); } // 函數(shù)式接口 @Test public void testFunction() { Function<Integer, Boolean> function = x -> x > 3; System.out.println(function.apply(4)); } }
自定義函數(shù)式接口
上面我們舉例A部門的四個員工,找出工資大于5000的員工。
// 使用策略式接口 @FunctionalInterface // 函數(shù)式接口(檢查)只能有一個抽象方法 public interface MyFilter<T> { /** * 獲取指定想要的employee對象 * * @param t * @return */ boolean getWant(T t); } /** * 策略設(shè)計模式 */ public List<Employee> needEmployee(List<Employee> employeeList, MyFilter<Employee> filter) { List<Employee> employees = new ArrayList<>(); for (Employee employee : employeeList) { if (filter.getWant(employee)) { employees.add(employee); } } return employees; } // 匿名內(nèi)部類 List<Employee> employees1 = needEmployee(employees, new MyFilter<Employee>() { @Override public boolean getWant(Employee employee) { return employee.getTotalSalary() >= 5000; } }); // 使用策略者設(shè)計模式Lambda簡化 needEmployee(employees, employee -> mployee.getTotalSalary() >= 5000);
看了上面代碼,如果還想簡化怎么做呢?這里可以使用java 8的Stream API可以大大簡化以上繁多的代碼。
employees.stream().filter(e -> e.getTotalSalary() > 5000d).map(Employee::getName).forEach(System.out::println);
看到這兒,可能剛剛?cè)腴T的同學(xué)會懵逼,因為上面用了Stream相關(guān)的API以及(Employee::getName)
中::表示什么含義呢?別著急,慢慢往下看。
方法引用
使用操作符::
將方法名和對象或者類的名字分隔開,組合有以下三種。
- 對象::實例方法名
- 類::靜態(tài)方法
- 類::實例方法
常見的x-> System.out.println()
等同于System.out::println。
注意:
- Lambda 體中調(diào)用方法的參數(shù)列表與返回值類型,要與函數(shù)式接口抽象方法的該函數(shù)列表和返回值類型保持一致。
- 若Lambda參數(shù)列表中的第一個參數(shù)是實例方法的調(diào)用者,而第二個參數(shù)是實例方法的參數(shù)時??梢允褂?ClassName :: method
說起來比較抽象,請看下面的例子。
// 對象::實例方法 @Test public void testConsumer() { Employee emp = new Employee(); // 函數(shù)式接口Supplier是空參,返回是Integer類型的接口, // 而對應(yīng)emp的實例方法getAge()剛剛好是空參且返回Integer類型的數(shù)據(jù),符合上面注意事項1的要求 Supplier<Integer> supplier = () -> emp.getAge(); Supplier<Double> sup2 = emp::getSalary; System.out.println(supplier.get()); } // 類::方法名 @Test public void testSupplier() { Comparator<String> comparator = (x, y) -> x.compareTo(y); // 要求參數(shù)第一個值作為方法體的調(diào)用者,第二個參數(shù)值作為方法體的被調(diào)用者(參數(shù))符合注意事項2的要求 Comparator<String> compString = String::compareTo; System.out.println(comparator.compare("2", "3")); Comparator<Integer> com = Integer::compare; System.out.println(com.compare(1, 2)); BiPredicate<String, String> predicate = String::equals; System.out.println(predicate.test("we", "eq")); }
構(gòu)造器引用
這兒以函數(shù)式接口為例:可以將返回的參數(shù)R,使用構(gòu)造器的構(gòu)造方法。
// 當(dāng)Employee有下面的構(gòu)造時候 public Employee(int age) { this.age = age; }
可構(gòu)造以下這樣的一個函數(shù)式接口。
Function<Integer,Employee> fun = i -> new Employee(i) System.out.println(fun.apply(13)); // 使用構(gòu)造器引用簡化后: Function<Integer,Employee> fun = Employee::new System.out.println(fun.apply(15));
到此Lambda簡單入門結(jié)束,下一篇將介紹java8新特性之Stream相關(guān)API。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot框架阿里開源低代碼工具LowCodeEngine
這篇文章主要為大家介紹了springboot框架阿里開源低代碼LowCodeEngine工具使用詳解有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06springboot實現(xiàn)獲取當(dāng)前服務(wù)器IP及當(dāng)前項目使用的端口號Port
這篇文章主要介紹了springboot實現(xiàn)獲取當(dāng)前服務(wù)器IP及當(dāng)前項目使用的端口號Port方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12詳解使用spring boot admin監(jiān)控spring cloud應(yīng)用程序
本篇文章主要介紹了詳解使用spring boot admin監(jiān)控spring cloud應(yīng)用程序,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11rabbitmq結(jié)合spring實現(xiàn)消息隊列優(yōu)先級的方法
本篇文章主要介紹了rabbitmq結(jié)合spring實現(xiàn)消息隊列優(yōu)先級的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02Java數(shù)據(jù)結(jié)構(gòu)之圖的原理與實現(xiàn)
圖(Graph)是由頂點的有窮非空集合和頂點之間邊的集合組成,通常表示為:G(V,E),其中,G表示一個圖,V是圖G中頂點的集合,E是圖G中邊的集合。本文將詳細(xì)介紹圖的原理及其代碼實現(xiàn),需要的可以參考一下2022-01-01關(guān)于Jedis的用法以及Jedis使用Redis事務(wù)
這篇文章主要介紹了關(guān)于Jedis的用法以及Jedis使用Redis事務(wù)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03使用CXF和Jersey框架來進(jìn)行Java的WebService編程
這篇文章主要介紹了使用CXF和Jersey框架來進(jìn)行Java的WebService編程,Web service是一個平臺獨立的低耦合的自包含的基于可編程的web的應(yīng)用程序,需要的朋友可以參考下2015-12-12