Java8學習筆記 — 【Lambda表示式】
1、Lambda表示式介紹
Lambda表示式是一個匿名函式,我們可以把Lambda表示式理解為是一段可以傳遞的程式碼(將程式碼像資料一樣傳輸),這樣就可以寫出更簡潔、更靈活的程式碼。作為一種更緊湊的程式碼風格,使Java語言表達能力得到提升。
2、Lambda表示式入門示例
需求:獲取公司中員工年齡大於35歲的員工資訊。
員工類:Employee.java
public class Employee { private int id; private String name; private int age; private double salary; public Employee() {} public Employee(int id) { this.id = id; } public Employee(int id, int age) { this.id = id; this.age = age; } public Employee(String name, int age, double salary) { this.name = name; this.age = age; this.salary = salary; } // setter、getter方法…… // toString方法…… }
需求實現:
實現方式一:迴圈遍歷&判斷
import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.TreeSet; import org.junit.Test; public class TestLambda { List<Employee> employees = Arrays.asList(new Employee("張三", 18, 6666.66), new Employee("李四", 39, 7777.77), new Employee("王五", 40, 8888.88), new Employee("趙六", 21, 9999.99), new Employee("周七", 42, 11111.11)); @Test public void test1() { List<Employee> list = filterEmployeesByAge(employees); for (Employee employee : list) { System.out.println(employee); } System.out.println("-----------------------"); List<Employee> list2 = filterEmployeeBySalary(employees); for (Employee employee : list2) { System.out.println(employee); } } /* * 需求1:獲取當前公司中員工年齡大於35的員工資訊 */ public List<Employee> filterEmployeesByAge(List<Employee> list) { List<Employee> emps = new ArrayList<Employee>(); for (Employee emp : list) { if (emp.getAge() > 35) { emps.add(emp); } } return emps; } /* * 需求2:獲取當前公司中員工工資大於7000的員工資訊 */ public List<Employee> filterEmployeeBySalary(List<Employee> list) { List<Employee> emps = new ArrayList<Employee>(); for (Employee emp : list) { if (emp.getSalary() > 7000) { emps.add(emp); } } return emps; } }
優化方式一:使用策略設計模式
定義策略介面:MyPredicate.java
public interface MyPredicate<T> {
boolean test(T t);
}
定義實現類:通過年齡過濾員工
FilterEmployeeByAge.java
public class FilterEmployeeByAge implements MyPredicate<Employee> { @Override public boolean test(Employee t) { return t.getAge() > 35; } }
定義實現類:通過薪資過濾員工
FilterEmployeeBySalary.java
public class FilterEmployeeBySalray implements MyPredicate<Employee> {
@Override
public boolean test(Employee t) {
return t.getSalary() > 7000;
}
}
測試:
@Test
public void test2() {
List<Employee> list = filterEmployeeByPolicy(employees, new FilterEmployeeByAge());
for (Employee employee : list) {
System.out.println(employee);
}
System.out.println("-------------------------------");
List<Employee> list2 = filterEmployeeByPolicy(employees, new FilterEmployeeBySalray());
for (Employee employee : list2) {
System.out.println(employee);
}
}
public List<Employee> filterEmployeeByPolicy(List<Employee> list, MyPredicate<Employee> mp) {
List<Employee> emps = new ArrayList<>();
for (Employee employee : list) {
if (mp.test(employee)) {
emps.add(employee);
}
}
return emps;
}
優化方式二:匿名內部類
@Test
public void test3() {
List<Employee> list = filterEmployeeByPolicy(employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee t) {
return t.getSalary() > 7000;
}
});
for (Employee employee : list) {
System.out.println(employee);
}
System.out.println("--------------------");
List<Employee> list2 = filterEmployeeByPolicy(employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee t) {
return t.getAge() > 35;
}
});
for (Employee employee : list2) {
System.out.println(employee);
}
}
優化方式三:Lambda表示式
@Test
public void test4() {
List<Employee> list = filterEmployeeByPolicy(employees, (e) -> e.getSalary() > 7000);
list.forEach(System.out::println);
System.out.println("--------------------");
List<Employee> list2 = filterEmployeeByPolicy(employees, (e) -> e.getAge() > 35);
list2.forEach(System.out::println);
}
優化方式四:Stream API
@Test
public void test7() {
employees.stream().filter((e) -> e.getSalary() > 7000).forEach(System.out::println);
System.out.println("-----------------------");
employees.stream().filter((e) -> e.getAge() > 35).forEach(System.out::println);
System.out.println("-----------------------");
employees.stream().map(Employee::getName).forEach(System.out::println);
}
3、Lambda表示式基礎語法
Java8中引入了一個新的操作符“ -> ”,該操作符稱為箭頭操作符或者Lambda操作符。箭頭操作符將Lambda表示式拆分成兩個部分:
左側:Lambda表示式的引數列表。
右側:Lambda表示式中所有需要執行的功能,即Lambda體。
語法格式一:無引數,無返回值
() -> System.out.println("HelloLambda!");
@Test
public void test1() {
int num = 0; // jdk1.8之前,必須加final修飾。jdk1.8可以不寫,預設會加上final
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!" + num);
}
};
r.run();
System.out.println("-----------------------");
Runnable r2 = () -> System.out.println("Hello Lambda!");
r2.run();
}
語法格式二:有一個引數,並且無返回值
(x)-> System.out.println(x);
@Test
public void test2() {
Consumer<String> con = (x) -> System.out.println(x);
con.accept("哈哈哈");
}
語法格式三:若只有一個引數,小括號可以省略不寫
x-> System.out.println(x);
語法格式四:有兩個以上的引數,有返回值,並且Lambda體中有多條語句
@Test
public void test3() {
Comparator<Integer> com = (x, y) -> {
System.out.println("函式式介面");
return Integer.compare(x, y);
};
}
語法格式五:若Lambda體中只有一條語句,return和大括號可以省略不寫
Comparator<Integer>com = (x, y) -> Integer.compare(x, y);語法格式六:Lambda表示式的引數列表的引數型別可以省略不寫,因為編譯器可以通過上下文推斷出資料型別,即“型別推斷”
@Test
public void test4() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
// 型別推斷:引數列表的引數型別可寫可不寫
Comparator<Integer> com2 = (Integer x, Integer y) -> Integer.compare(x, y);
}
Lambda表示式需要“函式式介面”的支援。
函式式介面:介面中只有一個抽象方法時,稱為函式式介面。可以使用@FunctionalInterface註解修飾,可以用來檢查被修飾的介面是否是函式式介面。
/**
* 函式式介面,使用@FunctionalInterface修飾,只能有一個抽象方法
*/
@FunctionalInterface
public interface MyFun {
Integer getValue(Integer num);
}
4、Java8內建的四大核心函式式介面
Consumer<T>:消費型介面
void accept(T t);
Supplier<T>:供給型介面
T get();
Function<T,R>:函式型介面
R apply(T t);
Predicate<T>:斷言型介面
boolean test(T t);
內建函式式介面演示:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.junit.Test;
public class TestLambda3 {
// Predicate<T>:斷言型介面
@Test
public void test4() {
List<String> list = Arrays.asList("abc", "javascript", "html", "sql", "spring");
List<String> result = filterStr(list, (str) -> str.length() > 3);
for (String ret : result) {
System.out.println(ret);
}
}
// 需求:將滿足條件的字串放入集合中去
public List<String> filterStr(List<String> list, Predicate<String> pre) {
List<String> strList = new ArrayList<>();
for (String str : list) {
if (pre.test(str)) {
strList.add(str);
}
}
return strList;
}
// Function<T,R>:函式型介面
@Test
public void test3() {
String ret = strHandler(" hello java ", (str) -> str.trim());
System.out.println(ret);
String subStr = strHandler("namespace", (str) -> str.substring(0, 4));
System.out.println(subStr);
}
// 需求:用於處理字串
public String strHandler(String str, Function<String, String> fun) {
return fun.apply(str);
}
// Supplier<T>:供給型介面
@Test
public void test2() {
List<Integer> list = getNumList(10, () -> (int) (Math.random() * 100));
for (Integer num : list) {
System.out.println(num);
}
}
// 需求:產生指定個數的整數,並放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
return list;
}
// Consumer<T>:消費型介面:
@Test
public void test1() {
happy(10000, (m) -> System.out.println("春節打年貨,平均每年消費:" + m + "元"));
}
public void happy(double money, Consumer<Double> con) {
con.accept(money);
}
}
5、方法引用與構造器引用
方法引用:若Lambda體中的內容有方法已經實現了,我們可以使用方法引用(可以理解為方法引用是Lambda表示式的另外一種表現形式)。
主要有三種語法格式:
物件::例項方法名
類::靜態方法名
類::例項方法名
注意:
1、Lambda體中呼叫方法的引數列表與返回值型別,要與函式式介面中抽象方法的函式列表和返回值型別保持一致。
2、若Lambda引數列表中的第一個引數是例項方法的呼叫者,而第二個引數是例項方法的引數時,可以使用ClassName::method程式碼示例:
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.junit.Test;
public class TestMethodRef {
// 類::例項方法名
@Test
public void test4() {
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp2 = String::equals;
}
// 類::靜態方法名
@Test
public void test3() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
Comparator<Integer> com2 = Integer::compareTo;
}
// 物件::例項方法名
@Test
public void test1() {
PrintStream ps1 = System.out;
Consumer<String> con = (x) -> ps1.println(x);
PrintStream ps = System.out;
Consumer<String> con1 = ps::println;
Consumer<String> con2 = System.out::println;
con2.accept("hello lambda");
}
@Test
public void test2() {
Employee emp = new Employee();
Supplier<String> sup = () -> emp.getName();
String name = sup.get();
System.out.println(name);
Supplier<String> sup2 = emp::getName;
String name2 = sup2.get();
System.out.println(name2);
}
}
構造器引用:
格式:ClassName::new
注意:需要呼叫的構造器的引數列表要與函式式介面中的抽象方法的引數列表保持一致。
程式碼示例:// 構造器引用
@Test
public void test5() {
Supplier<Employee> sup = () -> new Employee();
// 構造器引用方式
Supplier<Employee> sp2 = Employee::new; // 使用的無參構造器
Employee emp = sp2.get();
System.out.println(emp);
}
@Test
public void test6() {
Function<Integer, Employee> fun = (x) -> new Employee(x);
Employee emp = fun.apply(1);
System.out.println(emp);
// 構造器引用方式
Function<Integer, Employee> fun2 = Employee::new;
Employee emp2 = fun2.apply(100);
System.out.println(emp2);
BiFunction<Integer, Integer, Employee> bf = Employee::new;
Employee emp3 = bf.apply(202, 28);
System.out.println(emp3);
}
陣列引用:
格式:Type::new
程式碼示例:@Test
public void test7() {
Function<Integer, String[]> fun = (x) -> new String[x];
String[] arr = fun.apply(10);
System.out.println(arr.length);
Function<Integer, String[]> fun2 = String[]::new;
String[] arr2 = fun2.apply(20);
System.out.println(arr2.length);
}