一串代码

导航

JDK8新特性--函数式编程(lambda)

JDK8新特性:函数式编程(lambda表达式)

一、简介

如果说面向对象编程是对数据的抽象,那么函数式编程就是对行为的抽象

二、Lambda表达式

new Thread( () -> {System.out.println("lambda");} ).start();

2.1、函数式接口

1)函数式接口的定义
接口中只有一个未实现的方法,这样的接口可以作为“函数式接口”,通常习惯用注解@FunctionalInterface标注

@FunctionalInterface
public interface func{
      String hello(String name);

      /**
       * JDK8新特性,接口中可以有默认实现的方法啦!!!
       */
      default String great(){
          return "hi, I am a default function";
      }
}

个人理解:lambda表达式的出现只是为了简化函数式编程的开发

2.2、方法引用

双冒号 :: 为引用运算符,而它所在的表达式被称为方法引用。
如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者

private static void printStr(Consumer<String> consumer) {
      consumer.accept("Hello, World!");
}
public static void main(String[] args) {
      printStr(System.out::println);      // x -> System.out.println(x)
}

函数式接口是Lambda的基础,而方法引用是Lambda的孪生兄弟

三、函数式编程实例

JDK自带的函数式接口在java.util.function包下

3.1、Consumer

接收T对象,不返回值
1)作用:
消费某个对象
2)使用场景
Iterable接口的forEach方法需要传入Consumer,大部分集合类都实现了该接口,用于返回Iterator对象进行迭代。
3)设计思想
a、开发者调用ArrayList.forEach时,一般希望自定义遍历的消费逻辑,比如:输出日志或者运算处理等。
b、处理逻辑留给使用者,使用灵活多变。
c、多变的逻辑能够封装成一个类(实现Consumer接口),将逻辑提取出来。
4)案例

public class ConsumerTest {

    public static void main(String[] args) {
        ArrayList<Employee> employees = new ArrayList<>();
        String[] prefix = {"A", "B"};
        for (int i = 1; i <= 10; i++)
            employees.add(new Employee(prefix[i % 2] + i, i * 1000));
        employees.forEach(new SalaryConsumer());       
    }

    static class Employee {
        private String name;
        private int salary;

        public Employee() {
            this.salary = 4000;
        }
        public Employee(String name, int salary) {
            this.name = name;
            this.salary = salary;
        }
        //getter and setter       
    }
	
    // 输出需要交税的员工
    static class SalaryConsumer implements Consumer<Employee> {
        @Override
        public void accept(Employee employee) {
            if (employee.getSalary() > 2000) {
                System.out.println(employee.getName() + "要交税了.");
            }
        }        
    }    
}

3.2、Predicate

接收T对象并返回boolean
!)作用
判断对象是否符合某个条件
2)使用场景
ArrayList的removeIf(Predicate):删除符合条件的元素
如果条件硬编码在ArrayList中,它将提供无数的实现,但是如果让调用者传入条件,这样ArrayList就可以从复杂和无法猜测的业务中解放出来。
3)设计思想
提取条件,让条件从处理逻辑脱离出来,解耦合
4)案例

// employee.getSalary() > 2000 提取成一个条件类
class SalaryConsumer implements Consumer<Employee> {
     @Override
     public void accept(Employee employee) {
         // 自行传入本地的最低交税工资
         if (new SalaryPredicate(2000).test(employee)) {
             System.out.println(employee.getName() + "要交税了.");
         }
     }
 }

class SalaryPredicate implements  Predicate<Employee>{
    private int tax;

    public SalaryPredicate(int tax) {
        this.tax = tax;
    }

    @Override
    public boolean test(Employee employee) {
        return employee.getSalary() > tax;
    }
}

3.3、Function

接收T对象,返回R对象
1)作用
实现一个”一元函数“,即传入一个值经过函数的计算返回另一个值。
2)使用场景
V HashMap.computeIfAbsent(K , Function<K, V>) // 简化代码,如果指定的键尚未与值关联或与null关联,使用函数返回值替换。
Stream map(Function<? super T, ? extends R> mapper); // 转换流
3)设计思想
一元函数的思想,将转换逻辑提取出来,解耦合
4)案例

public static void main(String[] args) {
    ArrayList<Employee> employees = new ArrayList<>();
    String[] prefix = {"B", "A"};
    for (int i = 1; i <= 10; i++)
        employees.add(new Employee(prefix[i % 2] + i, i * 1000));
    int[] expenses = ListToArray(employees, new EmployeeToExpenses());// 公司对单个员工的支出数组
    int[] incomes = ListToArray(employees, new EmployeeToIncome()); // 单个员工的收入数组
    System.out.println("社保+公积金+税=" + (sum(expenses) - sum(incomes)) + "元");
}

private static int[] ListToArray(List<Employee> list, Function<Employee, Integer> function) {
    int[] ints = new int[list.size()];
    for (int i = 0; i < ints.length; i++)
        ints[i] = function.apply(list.get(i));
    return ints;
}

private static int sum(int[] salarys) {
    int sum = 0;
    for (int i = 0; i < salarys.length; i++)
        sum += salarys[i];
    return sum;
}

// 公司支出
static class EmployeeToExpenses implements Function<Employee, Integer> {

    @Override
    public Integer apply(Employee employee) {
        // 假设公司公积金和社保为工资的20%
        return Double.valueOf(employee.getSalary() * (1 + 0.2)).intValue();
    }

}

// 员工实际到手工资
static class EmployeeToIncome implements Function<Employee, Integer> {

    @Override
    public Integer apply(Employee employee) {
        // 假设员工薪水 * 80% 为到手工资
        return Double.valueOf(employee.getSalary() * (1 - 0.2)).intValue();
    }

}

3.4、Supplier

提供T对象(例如工厂),不接收值
1)作用
创建一个对象(工厂类)
2)使用场景
Optional.orElseGet(Supplier<? extends T>):当this对象为null,就通过传入supplier创建一个T返回。
3)设计思想
封装工厂创建对象的逻辑
4)案例

public static void main(String[] args) {
    // 生成固定工资的员工
    Supplier<Employee> supplier = () -> new Employee();
    Employee employee1 = supplier.get();
    employee1.setName("test1");
    Employee employee2 = supplier.get();
    employee2.setName("test2");
    System.out.println("employee1:" + employee1);
    System.out.println("employee2:" + employee2);
}

3.5、UnaryOperator

一元操作:接收T对象,返回T对象
1)作用
UnaryOperator继承了Function,与Function作用相同
不过UnaryOperator,限定了传入类型和返回类型必需相同
2)使用场景
List.replaceAll(UnaryOperator) // 该列表的所有元素替换为运算结算元素
Stream.iterate(T,UnaryOperator) // 重复对seed调用UnaryOperator来生成元素
3)设计思想
一元函数的思想,将同类转换逻辑提取出来,解耦合
4)案例

public static void main(String[] args) {
    ArrayList<Employee> employees = new ArrayList<>();
    String[] prefix = {"B", "A"};
    for (int i = 1; i <= 10; i++)
        employees.add(new Employee(prefix[i % 2] + i, i * 1000));
    System.o
        ut.println("公司进行薪资调整...");
    salaryAdjustment(employees,new SalaryAdjustment(4000));
    employees.forEach(System.out::println);
}

 static void salaryAdjustment(List<Employee> list, UnaryOperator<Employee> operator) {
     for (int i = 0; i < list.size(); i++) {
         list.set(i, operator.apply(list.get(i)));
     }
 }

static class SalaryAdjustment implements UnaryOperator<Employee> {
    private int salary;

    public SalaryAdjustment(int salary) {
        this.salary = salary;
    }

    @Override
    public Employee apply(Employee employee) {
        employee.setSalary(salary);
        return employee;
    }

}

posted on 2020-06-13 19:30  一串代码  阅读(603)  评论(0编辑  收藏  举报