概论

Java8 中引入 Lambda 表达式,使原本需要用匿名类实现接口来传递行为,现在通过 Lambda 可以更直观的表达。

  • Lambda表达式的本质实际上是“语法糖”,由编译器推断并帮助你将lambda表达式转换为常规代码,看起来更加简洁。
  • Lambda表达式是一个匿名函数,即没有函数名的函数,有些函数就只是临时一用,逻辑也很简单,就没必要给他单独写个函数体。
  • Lambda允许把函数作为一个方法的参数。、

Lambda表达式的语法:

/**
 * 语法格式一:无参数,无返回值。
 */
@Test
public void Test1() {
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello World!");
        }
    };
    r.run();

    System.out.println("---------------------");

    Runnable r1 = () -> System.out.println("Hello Lambda");
    r1.run();
}

/**
 * 语法格式二:有一个参数,无返回值
 * 语法格式三:若只有一个参数,那么参数的小括号,可以省略不写。
 */
@Test
public void Test2() {
    Consumer<String> c = (x) -> System.out.println("Hello " + x);
    c.accept("Consumer1");

    Consumer<String> c1 = x -> System.out.println("Hello " + x);
    c1.accept("Consumer2");
}

/**
 * 语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句。
 * 语法格式五:在四的基础上,如果Lambda体只有一条语句,那么return和大括号可以省略。
 */
@Test
public void Test3() {
    Comparator<Integer> com = (x,y) -> {
        System.out.println("函数式接口!");
        return Integer.compare(x,y);
    };

    Comparator<Integer> com1 = (x,y) -> Integer.compare(x,y);
}

认识Lambda表达式

假设现在有一张员工信息表,我们需要获取员工年龄大于35的员工信息
// 员工表
public class Employee {

    private String name;

    private Integer age;

    private Double salary;
    // 省略get/set方法
}

// 员工信息表
List<Employee> employees = Arrays.asList(
        new Employee("张三",23,9999.99),
        new Employee("李四",38,7777.77),
        new Employee("王五",55,3333.33),
        new Employee("赵六",18,6666.66)
);

// 需求:获取员工年龄大于35岁的员工信息
public List<Employee> filterEmployee(List<Employee> list) {
    List<Employee> result = new ArrayList<>();

    for (Employee employee : list) {
        if (employee.getAge() > 35) {
            result.add(employee);
        }
    }
    return result;
}

@Test
public void Test1() {
    List<Employee> employees = filterEmployee(this.employees);
    for (Employee employee : employees) {
        System.out.println(employee);
    }
}

// 需求变更:获取员工薪资大于4000的员工信息
public List<Employee> filterEmployeeBySalary(List<Employee> list) {
    List<Employee> result = new ArrayList<>();

    for (Employee employee : list) {
        if (employee.getSalary() > 4000) {
            result.add(employee);
        }
    }
    return result;
}

@Test
public void Test2() {
    List<Employee> employees = filterEmployeeBySalary(this.employees);
    for (Employee employee : employees) {
        System.out.println(employee);
    }
}

// 此时每需求变更一次,都要写一个新的方法,我们将其优化一下
// 优化方案一:策略设计模式
// 过滤接口
@FunctionalInterface
public interface MyFilterInterface<T> {

    boolean filter(T t);
}

// 过滤方法,每次传入不同的实现
public List<Employee> filterEmployee1(List<Employee> list, MyFilterInterface<Employee> myFilterInterface) {
    List<Employee> result = new ArrayList<>();

    for (Employee employee : list) {
        if (myFilterInterface.filter(employee)) {
            result.add(employee);
        }
    }
    return result;
}

// 过滤工资大于4000的
public class EmployeeBySalary implements MyFilterInterface<Employee>{
    @Override
    public boolean filter(Employee employee) {
        return employee.getSalary() > 4000;
    }
}

// 过滤出年龄大于35的
public class EmployeeFilterByAge implements MyFilterInterface<Employee>{

    @Override
    public boolean filter(Employee employee) {
        return employee.getAge() > 35;
    }
}

@Test
public void Test5() {
    List<Employee> employees = filterEmployee1(this.employees,new EmployeeFilterByAge());
    System.out.println("年龄大于35");
    for (Employee employee : employees) {
        System.out.println(employee);
    }

    List<Employee> employees1 = filterEmployee1(this.employees, new EmployeeBySalary());
    System.out.println("工资高于4000");
    for (Employee employee : employees1) {
        System.out.println(employee);
    }
}

// 优化方式二:匿名内部类,不用重新创建接口的实现类
@Test
public void Test6() {
    List<Employee> list = filterEmployee1(this.employees, new MyFilterInterface<Employee>() {
        @Override
        public boolean filter(Employee employee) {
            return employee.getAge() > 35;
        }
    });

    System.out.println("年龄大于35");
    for (Employee employee : list) {
        System.out.println(employee);
    }

    List<Employee> list2 = filterEmployee1(this.employees, new MyFilterInterface<Employee>() {
        @Override
        public boolean filter(Employee employee) {
            return employee.getSalary() > 4000;
        }
    });

    System.out.println("工资高于4000");
    for (Employee employee : list2) {
        System.out.println(employee);
    }
}

// 优化方法三:lambda表达式(在策略模式的基础上)
@Test
public void Test7() {
    List<Employee> employees = filterEmployee1(this.employees, (e) -> e.getAge() > 35);
    System.out.println("年龄大于35");
    employees.forEach(System.out::println);
}

函数式接口(SAM)single abstract method

从上面的示例就能看出,lambda表达式实现了MyFilterInterface接口,替代了匿名内部类,使代码看起来更加简洁了,而像MyFilterInterface这样有且只有一个抽象方法的声明的接口
叫做函数式接口。函数式接口是Java8实现Lmbada表达式的基础。
任意只包含一个抽象方法的接口,我们都可以用来做成Lambda表达式。每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。函数式接口与其他普通接口的区别:

  • 函数式接口中只能有一个抽象方法(这里不包括与Object的方法重名的方法)
  • 接口中唯一抽象方法的命名并不重要,因为函数式接口就是对某一行为进行抽象,主要目的就是支持 Lambda 表达式
  • 自定义函数式接口时,应当在接口前加上@FunctionalInterface标注(虽然不加也不会有错误)。编译器会注意到这个标注,如果你的接口中定义了第二个抽象方法的话,编译器会抛出异常。

Java内置函数式接口

Java8之前已有的函数式接口:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

Java 8之后新增加的函数接口:
java.util.function 包下,包含了很多接口,用来支持Java8的函数式编程,该包中的函数式接口有:
image.png

Lambda方法引用

/*
* 方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用"方法引用"
*           (可以理解为方法引用是Lambda表达式的另一种表现形式)
* 语法格式
*
* 对象::实例方法名
*
* 类::静态方法名
*
* 类::实例方法名
*
* 注意:
*  1.Lambda体中调用方法的参数列表与返回类型,要与函数式接口中抽象方法的函数列表和返回类型保持一致
*  2.若Lambda 参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时可以使用 ClassName::MethodName
*
* 构造器引用
*
*   语法格式
*   类名::new
* 注意:需要调用的构造器的参数列表需要与函数式接口中的抽象方法的参数列表保持一致
*
* 数组引用:
*   Type[]::new
*
*/

// 对象::实例方法名
@Test
public void Test1() {
    Consumer<String> con = x -> System.out.println(x);

    PrintStream out = System.out;
    Consumer<String> con1 = out::println;
    con1.accept("abcdef");
}

@Test
public void Test2() {
    Employee emp = new Employee("zhangsan",18,2222.22);
    Supplier<String> sup = emp::getName;
    System.out.println(sup.get());

    Supplier<Integer> sup1 = emp::getAge;
    System.out.println(sup1.get());
}

// 类::静态方法名
@Test
public void Test3() {
    Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
    Comparator<Integer> com1 = Integer::compareTo;
}

// 类::实例方法名
@Test
public void Test4() {
    BiPredicate<String,String> bip = (x,y) -> x.equals(y);

    BiPredicate<String,String> bip1 = String::equals;
}

// 构造器引用

public class Employee {

    private String name;

    private Integer age;

    private Double salary;

    public Employee() {}

    public Employee(String name) {
        this.name = name;
    }

    public Employee(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Employee(String name, Integer age, Double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    // 省略get/set方法
}
@Test
public void Test5() {
    Supplier<Employee> sup = () -> new Employee();

    Supplier<Employee> sup1 = Employee::new;
    Employee employee = sup1.get();
    System.out.println(employee);

    Function<String,Employee> fun = Employee::new;
    Employee apply = fun.apply("李四");
    System.out.println(apply);

    BiFunction<String,Integer,Employee> fun2 = Employee::new;
    Employee apply1= fun2.apply("王五", 20);
    System.out.println(apply1);
}

// 数组引用
@Test
public void Test6() {
    Function<Integer,String[]> fun = x -> new String[x];
    Function<Integer,String[]> fun1 = String[]::new;
    String[] apply = fun1.apply(10);
    System.out.println(apply.length);
}