一、Lambda表達(dá)式
Lambda表達(dá)式是一個(gè)匿名函數(shù) ,我們可以把Lambda 表達(dá)式理解為是一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣進(jìn)行傳遞)。有了Lambda表達(dá)式使得Java的函數(shù)式編程更加方便,代碼更加簡(jiǎn)潔。
二、Lambda表達(dá)式的例子
一個(gè)簡(jiǎn)單的例子
分別寫(xiě)兩個(gè)測(cè)試用例實(shí)現(xiàn)相同的功能,一個(gè)使用匿名內(nèi)部類,一個(gè)使用Lambda表達(dá)式,體會(huì)二者的差異。
需求:新建一個(gè)list數(shù)組,放入一些數(shù),對(duì)數(shù)組進(jìn)行排序并打印輸出。
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
import java.util.function.Consumer;
public class TestLambda {
List<Integer> list;
@Before
public void setup(){
list = new ArrayList<>();
list.add(3);
list.add(5);
list.add(99);
list.add(1);
}
@After
public void tearDown(){
list = null;
}
//匿名內(nèi)部類
@Test
public void test1(){
list.sort( new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
});
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
}
//Lambda表達(dá)式
@Test
public void test2(){
list.sort((o1, o2) -> Integer.compare(o1, o2));
list.forEach(integer -> System.out.println(integer));
}
}
可以看到在使用Lambda表達(dá)式能夠明顯減少代碼量,并且具有較好的可讀性。
一個(gè)更加復(fù)雜的例子
假設(shè)我們有一個(gè)Employee類如下:
class Employee{
private String name;
private int age;
private int salary;
public Employee() {
}
public Employee(String name, int age, int salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
現(xiàn)在需要對(duì)這個(gè)類進(jìn)行一些統(tǒng)計(jì)操作:
1、直接聲明函數(shù)實(shí)現(xiàn)統(tǒng)計(jì)功能。(無(wú)優(yōu)化)
需要對(duì)所有員工中年齡大于40的篩選出來(lái),我們可能會(huì)寫(xiě)一個(gè)篩選函數(shù)。
//一個(gè)更加復(fù)雜的例子
@Test
public void test3() {
List<Employee> employees = Arrays.asList(
new Employee("張三", 20, 12345),
new Employee("李四", 50, 23142),
new Employee("王二", 42, 2309),
new Employee("麻子", 35, 1233)
);
//需求:獲取年齡大于40的員工;
List<Employee> result1 = filterEmployees(employees);
for (Employee e : result1) {
System.out.println(e);
}
}
//獲取年齡大于40的員工;
public List<Employee> filterEmployees(List<Employee> list) {
List<Employee> employees = new ArrayList<>();
for (Employee employee : list) {
if (employee.getAge() >= 40) {
employees.add(employee);
}
}
return employees;
}
如果這樣的需求很多,例如要求篩選員工工資大于10000的,我們又需要添加一個(gè)篩選函數(shù)。
//需求:獲取當(dāng)前公司中員工工資大于10000的員工信息
public List<Employee> filterEmployees2(List<Employee> list) {
List<Employee> employees = new ArrayList<>();
for (Employee employee : list) {
if (employee.getSalary() >= 10000) {
employees.add(employee);
}
}
return employees;
}
可見(jiàn),每增加一個(gè)需求,就會(huì)新增一個(gè)篩選函數(shù)。仔細(xì)觀察會(huì)發(fā)現(xiàn),上面兩個(gè)篩選函數(shù)中只有if判斷語(yǔ)句中不一樣,因此可以將篩選函數(shù)提取到接口中,使代碼更加清晰。
2、提取函數(shù)公共部分,即使用策略設(shè)計(jì)模式(針對(duì)接口編程而不是實(shí)現(xiàn)編程)。
因?yàn)楹Y選函數(shù)中存在大量冗余代碼,將這些冗余代碼提出,并用接口方式實(shí)現(xiàn)。
定義泛型接口如下,當(dāng)中只有test一個(gè)泛型方法:
interface MyPredicate<T>{
boolean test(T t);
}
當(dāng)我們需要增加一個(gè)篩選功能時(shí),只需要新建一個(gè)類實(shí)現(xiàn)該接口,即實(shí)現(xiàn)test函數(shù)來(lái)添加篩選條件,例如篩選40歲以上員工的代碼如下:
class FilterEmployeeByAge implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getAge()>=40;
}
}
于是,我們可以針對(duì)所有的篩選定義一個(gè)統(tǒng)一的函數(shù),任何的篩選條件都通過(guò)調(diào)用該方法進(jìn)行篩選。
public List<Employee> fileterEmployee(List<Employee> list, MyPredicate<Employee> myPredicate) {
List<Employee> employees = new ArrayList<>();
for (Employee employee : list) {
if (myPredicate.test(employee)) {
employees.add(employee);
}
}
return employees;
}
具體使用方法如下:
@Test
public void test4(){
List<Employee> employees = Arrays.asList(
new Employee("張三", 20, 12345),
new Employee("李四", 50, 23142),
new Employee("王二", 42, 2309),
new Employee("麻子", 35, 1233)
);
List<Employee> employeeList = fileterEmployee(employees, new FilterEmployeeByAge());
for (Employee employee : employeeList) {
System.out.println(employee);
}
}
這樣一來(lái),所有的篩選都只需要調(diào)用filterEmployee這一個(gè)方法,只需要傳入過(guò)濾接口的不同實(shí)現(xiàn)類即可。
3、匿名內(nèi)部類??梢钥吹?,使用策略設(shè)計(jì)模式,雖然使得函數(shù)的公共部分被提取出來(lái),但針對(duì)每一種不同的篩選條件,仍然需要新建一個(gè)實(shí)現(xiàn)了特定接口的類。如果這個(gè)類僅使用一次,沒(méi)有被復(fù)用的機(jī)會(huì),顯然類的定義就顯得多余。于是,可以使用匿名內(nèi)部類進(jìn)行替換,我們不需要定義類來(lái)實(shí)現(xiàn)接口而直接使用new關(guān)鍵字新建一個(gè)滿足該接口(實(shí)現(xiàn)了該接口的方法)的對(duì)象。
例如使用匿名內(nèi)部類的方法篩選工資小于10000的員工。
@Test
public void test5(){
List<Employee> employees = Arrays.asList(
new Employee("張三", 20, 12345),
new Employee("李四", 50, 23142),
new Employee("王二", 42, 2309),
new Employee("麻子", 35, 1233)
);
List<Employee> employeeList = fileterEmployee(employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary()<10000;
}
});
for (Employee employee : employeeList) {
System.out.println(employee);
}
}
4、Lambda表達(dá)式。如果需要實(shí)現(xiàn)的接口僅有一個(gè)抽象方法,那么我們實(shí)現(xiàn)該接口時(shí)目的是明確的,就是為了實(shí)現(xiàn)該接口中定義的僅有的一個(gè)抽象方法的功能。所以匿名內(nèi)部類也可以省去,直接使用Lambda表達(dá)式傳遞“代碼”,告訴這個(gè)僅有的抽象方法應(yīng)該如何實(shí)現(xiàn)。上方的匿名內(nèi)部類也可以替換為如下Lambda表達(dá)式。函數(shù)式接口的聲明可以在接口上方加上@FunctionalInterface注解
@Test
public void test6(){
List<Employee> employees = Arrays.asList(
new Employee("張三", 20, 12345),
new Employee("李四", 50, 23142),
new Employee("王二", 42, 2309),
new Employee("麻子", 35, 1233)
);
List<Employee> employeeList = fileterEmployee(employees, employee -> employee.getSalary()<10000);
for (Employee employee : employeeList) {
System.out.println(employee);
}
}
5、Stream API。在Lambda表達(dá)式進(jìn)行幾乎極簡(jiǎn)優(yōu)化的情況下,Java8中還有一項(xiàng)新特性能夠更好的對(duì)集合進(jìn)行處理,即Stream API。通過(guò)調(diào)用Stream API提供的filter方法,甚至不需要定義上述的過(guò)濾方法和接口,直接結(jié)合Lambda表達(dá)式完成數(shù)據(jù)的篩選操作。
@Test
public void test7() {
List<Employee> employees = Arrays.asList(
new Employee("張三", 20, 12345),
new Employee("李四", 50, 23142),
new Employee("王二", 42, 2309),
new Employee("麻子", 35, 1233)
);
// List<Employee> employeeList = employees.stream().filter((e)->e.getSalary()<10000).collect(Collectors.toList());
// for (Employee employee : employeeList) {
// System.out.println(employee);
// }
employees.stream().filter((e)->e.getSalary()<10000).forEach(employee -> System.out.println(employee));
}
上方注釋可以進(jìn)行篩選并存儲(chǔ)到集合,或者直接使用forEach結(jié)合lambda表達(dá)式直接完成輸出,用起來(lái)更加方便。