关于java8新特性的理解
·速度更快
·代码更少(增加了新的语法Lambda表达式)--核心
·Stream API --- 核心
·便于并行
·最大化减少空指针异常Optional
Lambda表达式--匿名函数(一段可以传递的代码)
为什么需要Lambda表达式?
·在过去的Java中,我们无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法
·同时,在JavaScript中,函数的参数可以是一个函数,返回值也可以是另一个函数,这种情况十分常见,JavaScript是一门非常典型的函数式语言
我们可以看下关于匿名内部类使用lambda表达式和之前的对比
//原来的匿名内部类
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表达式
public void test2() {
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
TreeSet<Integer> ts = new TreeSet<>(com);
}
可以看到使用Lambda表达式后代码的简洁性和可读性都得到了提高
接下来我们通过一个实例来更近一步了解lambda表达式
定义一个员工实体类,我们的需求是通过不同的条件来过滤得到员工信息
public class Employee {
private String name;
private int age;
private double salary;
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public Employee() {
}
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{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
//向集合中添加元素
List<Employee> employees = Arrays.asList(
new Employee("张三", 18, 6000),
new Employee("李四", 21, 2000),
new Employee("孙悟空", 35, 8000),
new Employee("猪八戒", 40, 100000),
new Employee("唐僧", 55, 3000),
new Employee("孙悟饭", 18, 7000)
);
//需求:获取当前公司中员工年龄大于35的员工信息
public List<Employee> filterEmployee(List<Employee> list) {
List<Employee> emps = new ArrayList<>();
for (Employee emp : list) { //遍历list 如果大于35 就存入 emps
if (emp.getAge() >= 35) {
emps.add(emp);
}
}
return emps;
}
//需求:获取员工工资大于5000的员工信息
public List<Employee> filterEmployee2(List<Employee> list){
List<Employee> emps = new ArrayList<>();
for (Employee emp : list) { //遍历list 如果大于35 就存入 emps
if (emp.getSalary() >= 5000) {
emps.add(emp);
}
}
return emps;
}
可以看到,这样的代码十分冗余,而且耦合度过高,我们的本质要面向接口编程,
在一般情况下我们使用设计模式来进行代码优化(本例中使用策略模式)
首先定义一个MyPredicate接口,定义返回布尔类型的方法
public interface MyPredicate<T> {
public boolean test(T t);
}
定义接口的实现类FilterEmployeeByAge
public class FilterEmployeeByAge implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getAge() >= 35;
}
}
定义接口的实现类FilterEmployeeBySalary
public class FilterEmployeeBySalary implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getSalary() >= 5000;
}
}
具体实现方法
//优化方式一 俩个参数 一个所有员工信息的集合 一个条件判断的集合
public List<Employee> filterEmployee(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 Test4() {
List<Employee> list = filterEmployee(employees, new FilterEmployeeByAge());
for (Employee employee : list
) {
System.out.println(employee);
}
List<Employee> list2 = filterEmployee(employees, new FilterEmployeeByAge());
for (Employee employee : list2
) {
System.out.println(employee);
}
}
1.我们可以看到,在具体实现中代码量减少,而且符合了开闭原则(对修改关闭,对扩展开放),
如果需要增加新的需求,只需要新增接口的实现类即可。
2.实际上,我们可以看到这种优化方式的缺点:就是每次新增需求都需要定义新的实现类,有些多余,
实际上我们可以继续优化,通过使用匿名内部类来减少代码
//优化方式2 匿名内部类
@Test
public void test5() {
List<Employee> list = filterEmployee(employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary() <= 5000;
}
});
for (Employee employee : list
) {
System.out.println(employee);
}
}
1.我们可以看到,匿名内部类的本质就是在客户端(测试方法)中直接创建接口的实现类,
省去了单独创建实现类的麻烦。
2.但是实际上匿名内部类的写法依然代码冗余,可读性较低,我们可以采用lambda表达式来进一步优化
//优化方式三 lambda 表达式
@Test
public void test6() {
List<Employee> list = filterEmployee(employees, (e) -> e.getSalary() <= 5000);
list.forEach(System.out::println);
}
1.实际上,lambda表达式本真就是匿名函数,又进一步简化了匿名内部类的写法,这里的箭头个人感觉与JavaScript中一样,
毕竟JavaScript是典型的函数式语言,但是无论是匿名内部类还是lambda表达式,都只是在客户端中进行了优化,
就是省去了单独创建实现类的工作,将单独创建实现类改为了在客户端(测试方法)中创建。
2.但是本质上他们都需要依赖filterEmployee(List<Employee> list, MyPredicate<Employee> mp)方法来完成需求,
我们采用stream可以直接完成需求
//优化方式4 Stream
@Test
public void test7() {
employees.stream()
.filter((e) -> e.getSalary() >= 5000)
.forEach(System.out::println);
}
通过 流Stream这种新的集合运算和表达的高阶抽象,使用效果叹为观止,极大的简化了代码,
引用官方的一句话,Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。