最近看尚硅谷java8新特性视屏,总结一下学习知识。
Lambda表达式:是一个匿名函数,我们可以把Lambda理解为一段可以传递的代码(将代码像数据一样传递),可以写出更简洁更灵活的代码。作为一种更紧凑的风格,使java的表达能力得到了提升。
作为我这样的小白,看名词介绍是真的看不懂,下面贴上代码强化一下理解
package com.buerc.java8; 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 TestLambda1 { //使用原来的匿名对象方式 @Test public void test1() { Comparator<String> com=new Comparator<String>() { @Override public int compare(String o1, String o2) { return Integer.compare(o1.length(), o2.length()); } }; TreeSet<String> t=new TreeSet<>(com); t.add("hello"); t.add("world"); t.add("china"); t.add("Chinese"); System.out.println(t); System.out.println("------------------"); TreeSet<String> t1=new TreeSet<>(new Comparator<String>() { @Override public int compare(String o1, String o2) { return Integer.compare(o1.length(), o2.length()); } }); t1.add("china"); t1.add("hello"); t1.add("world"); t1.add("Chinese"); System.out.println(t1); } //现在使用lambda表达式 @Test public void test2() { Comparator<String> com=(x,y)->Integer.compare(x.length(), y.length()); TreeSet<String> t=new TreeSet<>(com); t.add("hello"); t.add("world"); t.add("china"); t.add("Chinese"); System.out.println(t); System.out.println("------------------"); TreeSet<String> t1=new TreeSet<>((x,y)->Integer.compare(x.length(), y.length())); t1.add("china"); t1.add("hello"); t1.add("world"); t1.add("Chinese"); System.out.println(t1); } //test1()和test2()对比很明显,lambda表达式一行就搞定, //用之前的匿名内部类需要多行代码但真正有用的核心代码就一行 //Integer.compare(o1.length(), o2.length()) //一组员工信息 List<Employee> list=Arrays.asList( new Employee(101, "张三", 18, 9999.99), new Employee(102, "李四", 20, 6666.66), new Employee(103, "王五", 25, 11111.11), new Employee(104, "赵六", 40, 55555.55), new Employee(105, "田七", 50, 99999.99) ); //查询员工年龄小于40岁的 public List<Employee> filterEmployee(List<Employee> list){ List<Employee> temp=new ArrayList<>(); for(Employee emp:list) { if(emp.getAge()<40) { temp.add(emp); } } return temp; } @Test public void test3() { List<Employee> temp=filterEmployee(list); for(Employee emp:temp) { System.out.println(emp); } } //查询工资大于10000的员工 public List<Employee> filterEmployee1(List<Employee> list){ List<Employee> temp=new ArrayList<>(); for(Employee emp:list) { if(emp.getSalary()>10000) { temp.add(emp); } } return temp; } @Test public void test4() { List<Employee> temp=filterEmployee1(list); for(Employee emp:temp) { System.out.println(emp); } } //如果后期有新的需求例如查询id范围内的员工,此时需要再次新增方法 //但是代码存在冗余,真正变化的就只有一行代码emp.getXXX这个条件的变化 //而且我们在扩展功能的时候,最好在不修改目标对象的功能前提下,对目标功能扩展. //针对新需求,新增方法显然对原代码进行了改动,这是需要尽量避免的 //所以我们有如下的优化方法 //优化方式一:策略设计模式 public List<Employee> filterEmployee( List<Employee> list,MyFilter<Employee> mf){ List<Employee> temp = new ArrayList<>(); for(Employee emp:list) { if(mf.test(emp)) {//这里针对具体的需求来重写MyFilter接口的test方法即可 temp.add(emp); } } return temp; } @Test public void test5() { List<Employee> temp1=filterEmployee(list,new EmployeeFilterByeAge()); for(Employee emp:temp1) { System.out.println(emp); } System.out.println("---------------------------"); List<Employee> temp2=filterEmployee(list,new EmployeeFilterByeSalary()); for(Employee emp:temp2) { System.out.println(emp); } //此种方法filterEmployee这个方法已经固定, //后期新需求也只需实现MyFilter重写test即可 //但是这种模式每次新增一个需求就得新建一个实现了MyFilter的类 } //优化方式二:匿名内部类 @Test public void test6() { List<Employee> temp1=filterEmployee(list,new MyFilter<Employee>() { @Override public boolean test(Employee t) { return t.getId()>103; } }); for(Employee emp:temp1) { System.out.println(emp); }//这种方法虽然filterEmployee方法已经固定,有新需求后不用新增方法, //也能进行功能扩充,也不会对原代码进行入侵,但是不够优雅 } //优化方式三Lambda 表达式 @Test public void test7() { List<Employee> temp1=filterEmployee(list,(e)->e.getAge()<40); temp1.forEach(System.out::println); System.out.println("-------------------"); List<Employee> temp2=filterEmployee(list,(e)->e.getSalary()>10000); temp2.forEach(System.out::println); } //优化方式四Stream API @Test public void test8() { list.stream() .filter((e)->e.getId()>103) .forEach(System.out::println); System.out.println("----------------------"); list.stream() .map(Employee::getName) .limit(3) .sorted() .forEach(System.out::println); } }
package com.buerc.java8; public class Employee { private int id; private String name; private int age; private double salary; public int getId() { return id; } public void setId(int id) { this.id = id; } 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 double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]"; } public Employee(int id, String name, int age, double salary) { super(); this.id = id; this.name = name; this.age = age; this.salary = salary; } public Employee() { super(); } }
package com.buerc.java8; //策略模式接口 public interface MyFilter<T> { boolean test(T t); }
package com.buerc.java8; public class EmployeeFilterByeAge implements MyFilter<Employee>{ @Override public boolean test(Employee t) { return t.getAge()<40; } }
package com.buerc.java8; public class EmployeeFilterByeSalary implements MyFilter<Employee>{ @Override public boolean test(Employee t) { return t.getSalary()>10000; } }
现在回过头来再来理解Lambda表达式
以前为了造个匿名内部类需要很多行代码,而真正我们有用的核心代码就一行,下图同样
使用Lambda表达式可以实现同样的效果,而且代码看起来更加优雅。因此Lambda表达式是一段可以传递的代码。
Lambda表达式语法:Lambda表达式在java语言中引入了一个新的语法元素和操作符“->”。称为Lambda操作符或箭头操作符,他将Lambda表达式分为了2部分
- 左侧:指定Lambda表达式需要的所有参数
- 右侧:指定Lambda体,即Lambda表达式要执行的功能
Lambda表达式语法:
* 左侧:Lambda 表达式的参数列表 * 右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体 * * 语法格式一:无参数,无返回值 * () -> System.out.println("Hello Lambda!"); * * 语法格式二:有一个参数,并且无返回值 * (x) -> System.out.println(x) * * 语法格式三:若只有一个参数,小括号可以省略不写 * x -> System.out.println(x) * * 语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句 * 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 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断” * (Integer x, Integer y) -> Integer.compare(x, y); * * 上联:左右遇一括号省 * 下联:左侧推断类型省 * 横批:能省则省
Lambda 表达式需要“函数式接口”的支持 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。 可以使用注解 @FunctionalInterface 修饰 可以检查是否是函数式接口
* Java8 内置的四大核心函数式接口 * * Consumer<T> : 消费型接口 * void accept(T t); * * Supplier<T> : 供给型接口 * T get(); * * Function<T, R> : 函数型接口 * R apply(T t); * * Predicate<T> : 断言型接口 * boolean test(T t);
package com.buerc.java8; 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 TestLambda2 { //Consumer<T> 消费型接口 : @Test public void test1() { play("购物",(str)->System.out.println(str)); //(str)->System.out.println(str)相当于重写了Consumer<T>的accept方法 } public void play(String entertainment,Consumer<String> c) { c.accept(entertainment); } //Supplier<T> 供给型接口 : @Test public void test2() { List<Integer> list=getNum(10,()->(int)(Math.random()*100)); list.forEach(System.out::println); } //需求:产生指定个数的整数,并放入集合中 public List<Integer> getNum(int count,Supplier<Integer> sup){ List<Integer> list=new ArrayList<Integer>(); for (int i = 0; i < count; i++) { Integer n=sup.get(); list.add(n); } return list; } //Function<T, R> 函数型接口: @Test public void test3() { System.out.println(strHandler("hello world",(str)->str.toUpperCase()));//具体处理操作是进行大写转换 } public String strHandler(String str,Function<String, String> fun) { return fun.apply(str);//将str字符串处理后返回 } //Predicate<T> 断言型接口: @Test public void test4() { List<String> list=Arrays.asList("hello","www","main","ok"); for (String string : list) { if(filterStr(string,(str)->str.length()>2)) {//具体判断操作是字符长度是否大于2 System.out.println(string); } } } public boolean filterStr(String str,Predicate<String> p) { return p.test(str);//进行字符串的判断操作 } }今天的学习总结就到这,接着继续看视频学习。