本文將簡單的介紹一下Lambda表達式和方法引用,這也是Java8的重要更新,Lambda表達式和方法引用最主要的功能是爲流(專門負責迭代數據的集合)服務.java
能夠把lambda表達式理解爲簡潔的匿名函數.redis
咱們先聲明一個函數式接口(函數式接口:就是隻有一個抽象方法的接口. lambda表達式和方法引用,只能用在函數式接口上),比較一下lambda表達式和匿名函數sql
public interface Animal { void cry(); public static void main(String [] args){ Animal dog = new Animal() { @Override public void cry() { System.out.println("狗: 汪汪叫"); } }; dog.cry();
Animal cat = () -> System.out.println("貓: 喵喵叫"); cat.cry(); } }
一個Animal的接口,裏面只有一個cry()的抽象方法, 分別用匿名函數和lambda表達式去實現這個接口. 使用lambda表達式的方法很是的簡潔,只須要一行.編程
lambda表達式語法: 參數 -> 具體的實現.ide
函數式接口的方法叫作函數描述符,lambda表達式的參數和實現必須和函數描述符的參數和返回值一一對應.cry()方法的參數和返回值都沒有因此lambda表達式就是 () -> System.out.println("貓: 喵喵叫");函數式編程
若是實現有多條語句的話,要寫在{}中,而且以;結尾.函數
() -> {測試
xxx;this
yyy;編碼
return "ccc";
}
列舉一個高端一點的使用lambda表達式的方法.以Oracle的Emp(員工表)爲例.
表結構
public class Emp { private BigDecimal empno; private String ename; private String job; private BigDecimal mgr; private Date hiredate; private Double sal; private BigDecimal comm; private BigDecimal deptno; public BigDecimal getEmpno() { return empno; } public void setEmpno(BigDecimal empno) { this.empno = empno; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename == null ? null : ename.trim(); } public String getJob() { return job; } public void setJob(String job) { this.job = job == null ? null : job.trim(); } public BigDecimal getMgr() { return mgr; } public void setMgr(BigDecimal mgr) { this.mgr = mgr; } public Date getHiredate() { return hiredate; } public void setHiredate(Date hiredate) { this.hiredate = hiredate; } public Double getSal() { return sal; } public void setSal(Double sal) { this.sal = sal; } public BigDecimal getComm() { return comm; } public void setComm(BigDecimal comm) { this.comm = comm; } public BigDecimal getDeptno() { return deptno; } public void setDeptno(BigDecimal deptno) { this.deptno = deptno; } }
如今咱們要寫一個方法,過濾全部工資在3000以上的員工(可能有的人可能會想,我直接寫sql不得了,費這麼多勁幹什麼,因此咱們如下的測試都假設數據是從redis查詢出來的.須要手動寫過濾條件)
public List<Emp> filter(List<Emp> listEmp){ List<Emp> filterList = new ArrayList<>(); for (Emp emp :listEmp) { if (emp.getSal()>3000){ filterList.add(emp); } } return filterList; }
這麼寫的壞處是條件硬編碼,若是光是改工資,咱們能夠把3000抽取爲一個參數,可是若是要將條件改成小於呢,若是過濾的是員工的工做呢.可能新手就會進行復制粘貼改一改條件,可是當重複的代碼達到必定的數量時,維護起來就是個災難.
咱們看看Java8提供的函數式編程,能夠怎麼解決這個方法.(固然使用匿名函數也能夠,可是不夠簡潔).
把變化的的條件抽取出去,變爲一個參數Predicate.具體的實現就是實現這個接口的test方法.
public List<Emp> filter1(List<Emp> listEmp, Predicate<Emp> predicate){ List<Emp> filterList = new ArrayList<>(); for (Emp emp :listEmp) { if (predicate.test(emp)){ filterList.add(emp); } } return filterList; }
咱們利用了java.util.function這個包提供的Predicate接口.這就是一個標準的函數式接口
測試一下咱們寫的過濾方法,分別按照工資和工做名稱進行過濾
1 List<Emp> filterSalEmp = empService.filter1(listEmp, Emp emp -> emp.getSal() > 3000); 2 List<Emp> filterJobEmp = empService.filter1(listEmp, Emp emp -> "SALMAN".equals(emp.getJob()));
Predicate接口的方法 boolean test(T t); 返回值是Boolean類型的,參數是任意類型 咱們的實現 Emp emp -> emp.getSal() > 3000 參數Emp ,返回Boolean類型的值 emp.getSal() > 3000 徹底知足. 能夠看到使用函數式接口編程提升了代碼的靈活性和可重用性.
其實lambda表達式的類型是能夠從上下文中本身推斷出來的,也就是說 上面的 lambda的參數 Emp emp 能夠不帶參數類型.寫成下面這樣
1 List<Emp> filterSalEmp = empService.filter1(listEmp, emp -> emp.getSal() > 3000); 2 List<Emp> filterJobEmp = empService.filter1(listEmp, emp -> "SALMAN".equals(emp.getJob()));
回到以前的例子:
String catCry = "貓: 喵喵叫";
Animal cat = () -> System.out.println(catCry);
cat.cry();
打印輸出:貓: 喵喵叫
lambda表達式可使用局部變量,可是必須是final類型的或事實上final類型的(不可改變).
<<java8實戰>>中的解釋:
第一,實例變量和局部變量背後的實現有一
個關鍵不一樣。實例變量都存儲在堆中,而局部變量則保存在棧上。若是Lambda能夠直接訪問局
部變量,並且Lambda是在一個線程中使用的,則使用Lambda的線程,可能會在分配該變量的線
程將這個變量收回以後,去訪問該變量。所以,Java在訪問自由局部變量時,其實是在訪問它
的副本,而不是訪問原始變量。若是局部變量僅僅賦值一次那就沒有什麼區別了——所以就有了
這個限制。
第二,這一限制不鼓勵你使用改變外部變量的典型命令式編程模式(咱們會在之後的各章中
解釋,這種模式會阻礙很容易作到的並行處理)。
方法引用能夠理解爲lambda表達式的快捷寫法,它比lambda表達式更加的簡潔,可讀性更高.有更好的重用性.若是實現比較簡單,一句話就能夠實現,複用的地方又很少推薦使用lambda表達式,不然應該使用方法引用.
方法引用的格式 類名::方法名
咱們使用方法引用的方式,從新實現上面剛剛過濾員工表的例子.
定義兩個條件類,方法的參數和返回值定義的和predicate的函數名描述符一致
public class EmpConditionA { public static boolean test(Emp emp) { return emp.getSal() > 3000; } }
public class EmpConditionB{
public static boolean test(Emp emp) {
return "engineer".equals(emp.getJob());
}
}
實現方式: 使用類名::方法的方式
List<Emp> listEmp = empService.listEmp(); List<Emp> filterSalEmp = empService.filter1(listEmp, EmpConditionA::test); List<Emp> filterJobEmp = empService.filter1(listEmp, EmpConditionB::test);
由於這個方法調用的是第三方類的方法因此是static的
還有兩種調用方式: 一種是直接調用流中的實例的方式,還有一種是調用局部變量的方式.
直接調用流中的實例的方式: 注意下面的Emp::getJob 就至關於集合中每個emp對象都調用本身的getJob方法.
這個例子是講將集合轉換爲流,map()方法能夠理解爲對集合的每個元素進行相應的操做,這裏就是對每個emp實例調用getJob方法.最後.collect(Collectors.toList())將流轉換爲新的list集合(關於流,筆者後面會繼續更新相關的博客).
listEmp.stream().map(Emp::getJob).collect(Collectors.toList());
調用局部變量的方式: 建立條件EmpconditionA的實例
EmpConditionA empConditionA = new EmpConditionA(); List<Emp> filterSalEmp = empService.filter1(listEmp, empConditionA::test);
好了關於lambda表達式和方法引用就簡單的介紹到這裏,
限於篇幅有些地方介紹的不是很詳細,若是有疑問歡迎你們隨時提問.