針對jdk1.8新特性,特來做個小結
一.Lambda表達式的使用
lambda表達式也叫函數式編程 :Lambda需要函數式接口支持,並且接口中的抽象方法只能有一個
函數式接口:接口中只有一個抽象方法的接口,稱之為函數式接口。可以使用@FunctionalInterface修飾,該注解可以檢查是否是函數式接口。
自己的理解就是可以 將lambda表達式向數據一樣傳遞
直接上代碼體會
//原來匿名內部類 @Test public void test1() { //比較器 Comparator<Integer> com = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } }; TreeSet<Integer> ts = new TreeSet<>(com); } //現在的 Lambda 表達式 @Test public void test2() { //比較器 Comparator<Integer> com = (x, y) -> Integer.compare(x, y); TreeSet<Integer> ts = new TreeSet<>(com); }
JDK1.8之前處理需求
1.new集合 2.遍歷 3.if判斷
//需求:獲取工資大於 5000 的員工的信息 public List<Employee> filterEmployeeBySalary(List<Employee> employees) { List<Employee> list = new ArrayList<>(); for (Employee employee : employees) { if (employee.getSalary() >= 5000) { list.add(employee); } } return list; }
可以發現弊端:當需求有變更 新增過濾條件 又需要遍歷一遍 if判斷 實際需要的代碼只有一句if (employee.getAge() > 18) 卻要寫大量冗余代碼 很惡心
//需求:獲取年齡大於 18 的員工的信息 public List<Employee> filterEmployeeByAge(List<Employee> employees) { List<Employee> list = new ArrayList<>(); for (Employee employee : employees) { if (employee.getAge() > 18) { list.add(employee); } } return list; }
優化思路:策略模式
1.定義接口 作為過濾條件
public interface MyPredicate<T> { public boolean test(T t); }
2.接口實現 設置過濾條件
public class FilterEmployeeBySalary implements MyPredicate<Employee>{ @Override public boolean test(Employee t) { return t.getSalary() >= 5000; } }
public class FilterEmployeeByAge implements MyPredicate<Employee> { @Override public boolean test(Employee t) { return t.getAge() > 18; } }
3.定義filter方法 傳入需要過濾集合,和過濾條件接口
//優化方式一:策略設計模式 public List<Employee> filterEmployees(List<Employee> employees, MyPredicate<Employee> mp) { List<Employee> list = new ArrayList<>(); for (Employee employee : employees) { if (mp.test(employee)) { list.add(employee); } } return list; }
4.測試
@Test public void test4() { List<Employee> list = filterEmployees(employees, new FilterEmployeeBySalary()); for (Employee employee : list) { System.out.println(employee); } System.out.println("---------------------------------"); List<Employee> list2 = filterEmployees(employees, new FilterEmployeeByAge()); for (Employee employee : list2) { System.out.println(employee); } }
使用匿名內部類簡化代碼
//優化方式二:策略設計模式 + 匿名內部類 @Test public void test5() { List<Employee> list = filterEmployees(employees, new MyPredicate<Employee>() { @Override public boolean test(Employee t) { return t.getSalary() >= 5000; } }); for (Employee employee : list) { System.out.println(employee); } }
使用lambda表達式 更加簡潔
//優化方式三:策略設計模式 + Lambda 表達式 @Test public void test6() { List<Employee> list = filterEmployees(employees, (e) -> e.getSalary() >= 5000); list.forEach(System.out::println); }
弊端:每增加一個過濾條件都需要寫一份實現類
能不能不寫接口和實現類就實現對集合的過濾?答案是肯定的
在JDK1.8中 引入流操作,使用Stream+Lambda表達式 極大簡化代碼
//優化方式四:Stream API @Test public void test7() { employees.stream() .filter((e) -> e.getSalary() >= 5000) .forEach(System.out::println); System.out.println("---------------------------------------"); employees.stream() .map(Employee::getName) .forEach(System.out::println); }
lambda表達式的基本使用規則:
/** * lambda表達式 * 左側:Lambda表達式的參數列表 * 右側:Lambda表達式所執行的功能,即lambda體 * 接口中只能有一個實現類 */ @Test public void test8() { //Consumer接口中只有一個抽象方法,和一個接口默認方法 Consumer<String> con = (x) -> System.out.println("Yan:" + x); con.accept("我是YanYan啊"); } @Test public void test9() { //lambda體中有多條語句,必須使用大括號 Comparator<Integer> com = (x, y) -> { System.out.println("Comparator接口"); return x.compareTo(y); }; com.compare(1, 2); }
//原來匿名內部類 @Test public void test1() { //比較器 Comparator<Integer> com = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } }; TreeSet<Integer> ts = new TreeSet<>(com); } //現在的 Lambda 表達式 @Test public void test2() { //比較器 Comparator<Integer> com = (x, y) -> Integer.compare(x, y); TreeSet<Integer> ts = new TreeSet<>(com); }
//Demo1
@FunctionalInterface interface MyFun { Integer getValue(Integer num); } public Integer operation(Integer num, MyFun mf) { return mf.getValue(num); } @Test public void test10() { System.out.println(operation(10, (x) -> x * x)); System.out.println(operation(1, x -> x + x)); }
//Demo2
@FunctionalInterface interface HandleStr { String handle(String str); } public String strHandle(String str, HandleStr hs) { return hs.handle(str); } @Test public void test2() { String str = "zhangyan"; System.out.println( strHandle(str, x -> { //轉換成大寫 return x.toUpperCase().substring(1, 3); })); }
//Demo3
@FunctionalInterface interface MyFun3<T, R> { R getValue(T t1, T t2); } public void op(Long l1, Long l2, MyFun3<Long, Long> mf) { System.out.println(mf.getValue(l1, l2)); } @Test public void test3() { op(100l,200l,(x,y)-> x+y); op(10l,15l,(x,y)->{ return x*y; }); }
二.JAVA內置四大核心函數式接口
之前我們在使用Lamda表達式中每次都需要自己寫一個接口,然后再去自定義接口的實現類
然而,JKD1.8已經為我們定義了四大函數式接口,配合Lambda表達式使用起來非常方便
/** * java內置四大核心函數式接口 * <p> * 1.Consumer<T>消費型接口 無返回值 void accept(T t) * 2.Supplier<T>供給型接口 T get(); * 3.Function<T>函數型接口 R apply(T t) * 4.Predicate<T>斷言型接口 用於做判斷操作 boolean test(T t) */ public class TestLambda3 { //Consumer<T> @Test public void test1() { happy(10.15, (x) -> System.out.println(x)); } //happy public void happy(double money, Consumer<Double> con) { con.accept(money); } //Supplier<T> 產生指定個數的隨機數,存入集合 @Test public void test2() { this.getNumber(10, () -> (int) Math.random() * 100); } public List<Integer> getNumber(int num, Supplier<Integer> sup) { List<Integer> list = new ArrayList<>(); for (int i = 0; i < num; i++) { list.add(sup.get()); } return list; } //Predicate<T> @Test public void test3() { List<String> strList = Arrays.asList("Mike", "Lee", "Yan"); //使用時設置條件 List<String> list = this.filterStr(strList, (x) -> x.length() > 3); list.forEach(System.out::print); } //將滿足條件的字符串 存入集合中 public List<String> filterStr(List<String> str, Predicate<String> pre) { List<String> list = new ArrayList<>(); for (String s : str) { if (pre.test(s)) { list.add(s); } } return list; }
三.方法引用和構造器引用
本質上是lambda表達式的另一種表現形式
* 三種語法格式
* 1.對象 :: 實例方法名
*
* 2.類 :: 靜態方法名
*
* 3.類 :: 實例方法名
*/
public class TestMethodRef { @Test public void test1() { Consumer<String> consumer = (x)-> System.out.println(x); PrintStream ps = System.out; Consumer<String> son = ps::println; }
@Test public void test2() { Employee emp = new Employee("Yan", 24, '男', 14000); Supplier<String> sup = emp::getName; /* Supplier<String> sup2 = new Supplier<String>() { @Override public String get() { return emp.getName(); } };*/ // 與上面語句功能相同 Supplier sup2 = ()->emp.getName(); String s = sup.get(); System.out.println(s); } //類 :: 靜態方法 @Test public void test3() { Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y); Comparator<Integer> com2 = Integer::compare; } //類 :: 實例方法名 @Test public void test4() { BiPredicate<String, String> bp1 = (x, y) -> x.equals(y); //限制條件:只有當(x, y) -> x.equals(y) 第一個參數x作為參數的調用者,第二個參數y為方法參數時,才可以使用這種方式 //ClassName::method BiPredicate<String, String> bp2 = String::equals; } //構造器引用 ClassName::new @Test public void test5() { Supplier<Employee> sup1 = () -> new Employee(); Employee employee = sup1.get();//接口sup1 調用方法 get() get方法被() -> new Employee() 重寫 //調用無參構造器 因為 需要和Supplier get方法中的入參和返回值類型保持一致 Supplier<Employee> sup2 = Employee::new; //如需使用其他構造器則 BiFunction<String, Integer, Employee> bg1 = (x, y) -> new Employee(x, y); System.out.println(bg1.apply("Yan", 23)); //調用有參構造器,取決於BiFunction中的apply方法的入參 BiFunction<String, Integer, Employee> bg2 = Employee::new; } //數組引用 type[] :: new @Test public void test6() { Function<Integer, String[]> fun1 = (x) -> new String[x]; String[] apply = fun1.apply(10); System.out.println(apply); Function<Integer, String[]> fun2 = String[]::new; }