一、什麼是Lambda表達式
最近研究了一下java8的一個新特性lambda表達式,發現使用lambda表達式确實能夠簡化我們的代碼是的代碼的邏輯更加清晰和簡潔。以下是我整理的關于lambda表達式的知識點,在這裡分享給大家,如有寫的不對的地方請指出。
Lambda表達式是Java8的一個新特性,它提供了一種更加清晰和簡明的方式使用Functional Interface。在代碼中經常會出現各種各樣的匿名内部類,使得代碼變的臃腫、混亂,而lambda就是一種優雅的方式,它簡化對于Functionla Interface的使用,而不需要定義匿名内部類。
二、知識背景
Annoymous Inner class
在Java裡面,可以在使用的時候聲明一個類的對象,并在使用時給出類的某些方法的實作,這種類叫做匿名内部類,比如在Android中的OnClickListener,代碼如下:
如上代碼所示,new View.OnClickListener()就是一個匿名内部類。
Functional interface
功能接口,隻含有一個抽象方法的接口,即SAM(Simple Abstract Method),單一抽象方法。這樣的接口比如:
- 檔案過濾接口-java.io.FileFilter public abstract boolean accept(File pathname);
- 比較接口-java.lang.Comarable int compareTo(T another);
- Android的OnClickListener接口-android.view.View.OnClickListener onClick(View v);
三、Lambda表達式應用
了解了匿名内部類和功能接口下面來介紹一下lambda表達式。lambda是一種能夠極大的簡化聲明和使用功能性接口的表達式文法,通過使用lambda表達式,可以使得代碼更加的清晰和簡潔。
Lambda Syntax
每個lambda表達式都包括以下三個部分:
Argument List-(int x,int y)
Arrow Token - ->
Body - x+y
Argument部分是調用SAM方法時的入口參數;Arrow Token固定為->;Body内部可以是簡單的表達式,也可是複雜的代碼塊。以上的例子僅僅是一個簡單的表達式,用來傳回輸入的x和y的和,如果參數不比對或者不是單一抽象接口都會出現編譯錯誤。
Lambda Examples
1. Runnable Lambda
使用匿名内部類代碼如下:
public static void testRunnable(){
Thread thread = new Thread(new Runnable(){
System.out.println(“In ”);
});
}
使用lambda表達式代碼如下
public static void testRunnable(){
Thread thread = new Thread(() -> {
System.out.println(“In ”);
});
}
2. FileFilter
使用匿名内部類代碼如下:
public static void testFileFilter(String path){
File file = new File(path);
FileFilter filter = new FileFilter(){
public boolean accept(File pathName){
return pathName.isDirectory();
}
};
File[] files = file.listFiles(filter);
}
public static void testFileFilter(String path){
File file = new File(path);
file.listFiles((file)->file.isDirectory);
}
3.Comparator
private static void testComparator(){
Person person1 = new Person();
person1.setAge(10);
Person person2 = new Person();
person2.setAge(90);
Person person3 = new Person();
person3.setAge(26);
List<Person> persons = new ArrayList<>();
persons.add(person1);
persons.add(person2);
persons.add(person3);
Collections.sort(persons,(p1,p2) -> p1.getAge() - p2.getAge());
for(Person p : persons){
System.out.println(p.getAge());
}
}
在這個例子當中,lambda作為Comparator參數傳入sort方法,有人可能會有疑問,Comparator接口中并非隻有一個simple abstract method ,即該接口不是剛才說過的Functional Interface。我們打開java.lang.Comparator接口,發現接口名字上有個标注@Functational Interface,進一步看這個标注的詳細介紹會發現,該接口是從java8開始加入的,目的是聲明某個接口為Functional Interface。如果該接口不是Functional Interface,即擁有超過一個抽象方法,則會抛出編譯錯誤。而這裡Comparator中還有一個抽象方法名為boolean equals(Object obj),從文檔中可以看出,如果某個接口擁有一個抽象方法,同時又覆寫了Object的public方法,那麼這個抽象方法并不會計算在該接口的抽象方法數量中。也就是說該接口還是一個 Functional Interface。
四、Lambda Advantages
通過以上的幾個例子,大家對lambda已經有了一定的認識,lambda除了能夠簡化代碼行數之外,還能夠使得我們的代碼遵從Don’ Repeat Yourself(DRY)準則,提高代碼的可讀性和簡潔程度。
(1)情景:現在某公司年底需要給公司的員工發一些獎勵,獎勵程度和在公司工作的年數有關,規則如下:a.工作1年以内的-發放2000元;b.工作1-3年的-發放5000元;c.工作3-5年的-發放10000元;d.工作5年以上的-發放20000元。
(2)代碼初步實作,如下:
//定義實體類:Staff.java
public class Staff{
private int staffId;
private int workYears;
private int name;
public int getWorkYears(){
return this.workYears;
}
public String getName(){
}
public void setWorkYears(int workYears){
this.workYears = workYears;
}
public void setName(String name){
this.name = name;
}
}
//定義生産資料的靜态方法
public class StaffUtil{
public static List<Staff> createStaffList(int count){
List<Staff> staffList = new ArrayList<Staff>();
for(int i=0;i<count;i++){
Staff staff = new Staff();
staff.setName(“Kevin”+i);
staff.setWorkYears(i);
staffList.add(staff);
}
}
}
//定義測試類
public class TestLambda{
public static void main(String[] args){
List staffList = StaffUtil.createStaffList(20);
filterA(staffList);
}
public static void filterA(List<Staff> staffList){
for(Staff staff:staffList){
if(staff.getWorkYears() <= 1){
sendMoney(staff,2000);
}
}
}
public static void filterB(List<Staff> staffList){
for(Staff staff:staffList){
if(staff.getWorkYears() <= 3 && staff.getWorkYears() > 1){
sendMoney(staff,5000);
}
}
}
public static void filterC(List<Staff> staffList){
for(Staff staff:staffList){
if(staff.getWorkYears() <= 5 && staff.getWorkYears() > 3){
sendMoney(staff,10000);
}
}
}
public static void filterD(List<Staff> staffList){
for(Staff staff:staffList){
if(staff.getWorkYears() <= 5 && staff.getWorkYears() > 3){
sendMoney(staff,10000);
}
}
}
public static void sendMoney(Staff staff,int money){
System.out.println(“發放”+staff.getName()+”獎金”+money+”元”);
}
}
以上代碼邏輯清晰,但是有些地方還需要繼續優化:
1.代碼違背了DRY準則,每個方法内部都會做一次循環
2.對于每種規則都會有一個方法與之對應,每種情況都需要實作一個方法與之對應
3.代碼不夠靈活,可擴充性不高“
(3)第一次修改
以下代碼我們嘗試将判斷條件抽出來,放到另一個單獨的方法中
public class TestLambda1{
public static void main(String[] args){
List staffList = StaffUtil.createStaffList(20);
filterA(staffList);
}
public static void filterA(List<Staff> staffList){
for(Staff staff:staffList){
if(isA(staff)){
sendMoney(staff,2000);
}
}
}
public static void filterB(List<Staff> staffList){
for(Staff staff:staffList){
if(isB(staff)){
sendMoney(staff,5000);
}
}
}
public static void filterC(List<Staff> staffList){
for(Staff staff:staffList){
if(isC(staff)){
sendMoney(staff,10000);
}
}
}
public static void filterD(List<Staff> staffList){
for(Staff staff:staffList){
if(isD(staff)){
sendMoney(staff,10000);
}
}
}
public static boolean isA(Staff staff){
return staff.getWorkYears() <= 1;
}
public static boolean isB(Staff staff){
return staff.getWorkYears() <= 3 && staff.getWorkYears() > 1;
}
public static boolean isC(Staff staff){
return staff.getWorkYears() <= 5 && staff.getWorkYears() > 3;
}
public static boolean isD(Staff staff){
return staff.getWorkYears() <= 5 && staff.getWorkYears() > 3;
}
public static void sendMoney(Staff staff,int money){
System.out.println(“發放”+staff.getName()+”獎金”+money+”元”);
}
}
代碼将判斷條件抽出來放到方法裡面是的每個判斷條件得以重用,這是相對之前代碼一個提高。那麼對于判斷條件的處理有沒有更好的方法呢?
(4)匿名内部類
定義一個判斷員工工作年限的接口,如下:
public interface MyTest<T>{
boolean test(T t);
}
public class TestLambda1{
public static void main(String[] args){
List staffList = StaffUtil.createStaffList(20);
filterA(staffList,new MyTest<Staff>(){
@Override
boolean test(Staff staff){
return staff.getWorkYears() <= 1;
}
});
filterA(staffList,new MyTest<Staff>(){
@Override
boolean test(Staff staff){
return staff.getWorkYears() <= 3 && staff.getWorkYears() > 1;
}
});
//…
}
public static void filterA(List<Staff> staffList,MyTest<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,2000);
}
}
}
public static void filterB(List<Staff> staffList,MyTest<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,5000);
}
}
}
public static void filterC(List<Staff> staffList,MyTest<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,10000);
}
}
}
public static void filterD(List<Staff> staffList,MyTest<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,10000);
}
}
}
public static void sendMoney(Staff staff,int money){
System.out.println(“發放”+staff.getName()+”獎金”+money+”元”);
}
}
上面的代碼使用匿名内部類來代替剛才的方法,好處時,減少了三個方法,而且對于判斷規則,沒必要寫死,隻要在調用時給出規則即可。但是缺點是使用匿名内部類,使得代碼變得臃腫無比。
(5)使用優雅的Lambda表達式
前面(4)定義了一個MyTest接口,該接口隻有一個抽象方法test,結合我們前面講到的Functional Interface,是以該接口是支援lambda表達式的。其實我們定義這個方法是沒有必要的,因為java中已經定義了類似的接口供我們使用:Java.util.function.Predicate。
public class TestLambda1{
public static void main(String[] args){
List staffList = StaffUtil.createStaffList(20);
Predicate<Staff> preA = staff -> staff.getWorkYears() <= 1;
Predicate<Staff> preB = staff -> staff.getWorkYears() <= 3 && staff.getWorkYears() > 1;
filterA(staffList,preA);
filterA(staffList,preB);
//…
}
public static void filterA(List<Staff> staffList,Predicate<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,2000);
}
}
}
public static void filterB(List<Staff> staffList,Predicate<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,5000);
}
}
}
public static void filterC(List<Staff> staffList,Predicate<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,10000);
}
}
}
public static void filterD(List<Staff> staffList,Predicate<Staff> test){
for(Staff staff:staffList){
if(test(staff)){
sendMoney(staff,10000);
}
}
}
public static void sendMoney(Staff staff,int money){
System.out.println(“發放”+staff.getName()+”獎金”+money+”元”);
}
}
怎麼樣代碼是不是變的優雅許多了呢?