JDK8函数式编程

概述

  开发当中一直都有在使用函数式编程,尤其是在Stream类时,今天就来对jdk8中的函数式编程来做一个学习汇总。
  我们最常用的面向对象编程(Java)属于命令式编程(Imperative Programming)这种编程范式。常见的编程范式还有逻辑式编程(Logic Programming),函数式编程(Functional Programming)。函数式编程作为一种编程范式,在科学领域,是一种编写计算机程序数据结构和元素的方式,它把计算过程当做是数学函数的求值,而避免更改状态和可变数据。

Lambda 表达式的形式

  在介绍函数式编程之前,让我们来看一下jdk8中提供的Lambda表达式的五种形式:

1.
Runnable noArguments = () -> System.out.println("Hello World");

2.
ActionListener oneArgument = event -> System.out.println("button clicked");

3.
Runnable multiStatement = () -> {
    System.out.print("Hello");
    System.out.println(" World");
};

4.
BinaryOperator add = (x, y) -> x + y;

5.
BinaryOperator addExplicit = (Long x, Long y) -> x + y;

  1中所示的 Lambda表达式不包含参数,使用空括号 () 表示没有参数。该 Lambda 表达式 实现了 Runnable 接口,该接口也只有一个 run 方法,没有参数,且返回类型为void。2中所示的Lambda表达式包含且只包含一个参数,可省略参数的括号。Lambda表达式的主体不仅可以是一个表达式,而且也可以是一段代码块,使用大括号 ({})将代码块括起来,如3所示。该代码块和普通方法遵循的规则别无二致,可以用返回或抛出异常来退出。只有一行代码的Lambda表达式也可使用大括号,用以明确Lambda表达式从何处开始、到哪里结束。Lambda表达式也可以表示包含多个参数的方法,如4所示。这时就有必要思考怎样去阅读该Lambda 表达式。这行代码并不是将两个数字相加,而是创建了一个函数,用来计算两个数字相加的结果。变量add的类型是BinaryOperator。当需要限定使用该Lambda表达式的使用范围,比如说只能传递Long型的参数时,则可以将参数具体化为Long型,如5。
我们以Spring中创建一个自定义的事件监听器为例:

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);
}

  ApplicationListener 只有一个抽象方法:actionPerformed,被用来表示行为:接受一个参数,返回空。当我们要自定义一个自己的ApplicationListener时,可以通过匿名内部列的形式实现:

@SpringBootApplication
public class App {

    private static final Logger log = LoggerFactory.getLogger(App.class);

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Bean
    public ApplicationListener newApplicationListener() {
        System.out.println("create a ApplicationListener");
        return new ApplicationListener() {
            @Override
            public void onApplicationEvent(ApplicationEvent event) {
                System.out.println("process event");
            }
        };
    }
}

  当然,还可以使用上文提到的Lambda表达式的形式

@SpringBootApplication
public class App {

    private static final Logger log = LoggerFactory.getLogger(App.class);

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Bean
    public ApplicationListener newApplicationListener() {
        System.out.println("create a ApplicationListener");
        return event -> {
            System.out.println("process event");
        };
    }
}

  一个限制是,函数接口里面只能有一个抽象方法

函数接口

  其实在上一节当中,我们已经使用了函数接口。jdk中为我们提供了@FunctionalInterface注解,该注解的作用是,当我们对一个接口进行标注时,编译器会检查该接口是否只用一个抽象方法。也就是说,即使我们的接口不标注该注解,只要满足接口只有一个抽象函数的要求,就是一个函数接口。ApplicationListener就是一个函数接口。

  JDK8中提供了一组常用的核心函数接口:

接口 参数 返回类型 描述
Predicate T boolean 用于判别一个对象。比如求一个人是否为男性
Consumer T void 用于接收一个对象进行处理但没有返回,比如接收一个人并打印他的名字
Function T R 转换一个对象为不同类型的对象
Supplier None T 提供一个对象
UnaryOperator T T 接收对象并返回同类型的对象
BinaryOperator (T, T) T 接收两个同类型的对象,并返回一个原类型对象

除此之外,还有其他一些很多的函数接口,这些接口都在java.util.function包下
JDK8函数式编程_第1张图片
  任意点开一个接口,我们都可以发现,该接口只会有一个抽象函数,0个或者多个default函数。
  在大多数场景中,我们使用函数接口进行编程主要是在Stream中,打开Stream的类可以发现,该类的方法参数很多都是使用到了函数接口,以Stream类foreach为例:

public static void main(String[] args) {
        List stringList = new ArrayList<>();
        stringList.add("1");
        stringList.add("2");
        stringList.stream().forEach((value) -> {
            System.out.println(value);
        });
    }

  其实质是:

public static void main(String[] args) {
        List stringList = new ArrayList<>();
        stringList.add("1");
        stringList.add("2");
        Consumer stringConsumer = (value) -> {
            System.out.println("value is : " + value);
        };
        stringList.stream().forEach(stringConsumer);
    }

方法引用

  在使用Stream的过程中,我们也许还会见到下面这种情况:

public class App {

    private static final Logger log = LoggerFactory.getLogger(App.class);

    public static void main(String[] args) {
        List list = Lists.newArrayList(3, 5, 2, 9, 1);
        int maxInt = list.stream()
                .max(Integer::compareTo)
                .get();
        assertEquals(maxInt, 9);

        List list2 = Lists.newArrayList(-3, 5, -2, 9, -1);
        assertEquals(list2.stream().map(Math::abs).allMatch(e -> e > 0), true);

        Comparator comparator = new Comparator() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge().compareTo(o2.getAge());
            }
        };
        List PersonList = Lists.newArrayList(new Person("teny",23), new Person("tome",55));
        Person minAgePerson = PersonList.stream()
                .min(comparator::compare)
                .get();
        assertEquals(minAgePerson.getAge(), 23);

        Person newPerson = Person.createPerson(Person::new);
    }

    public static class Person {
        private String name;
        private Integer age;

        public static Person createPerson(Supplier supplier) {
            return supplier.get();
        }

        public Person() {
            this.name = "";
            this.age = -1;
        }

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

        public String getName() {
            return name;
        }

        public Integer getAge() {
            return age;
        }
    }
}

Integer::compareTo也是属于Java8引入的新特性,叫做方法引用(Method References),其实就是 (int1, int2) -> int1.compareTo(int2) 的简写。
  引用方法有下面几种方式

对象引用::实例方法名

Comparator comparator = new Comparator() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge().compareTo(o2.getAge());
            }
        };
        List PersonList = Lists.newArrayList(new Person("teny",23), new Person("tome",55));
        Person minAgePerson = PersonList.stream()
                .min(comparator::compare)
                .get();
        assertEquals(minAgePerson.getAge(), 23);

类名::静态方法名

List<Integer> list2 = Lists.newArrayList(-3, 5, -2, 9, -1);
        assertEquals(list2.stream().map(Math::abs).allMatch(e -> e > 0), true);

类名::实例方法名

List<Integer> list = Lists.newArrayList(3, 5, 2, 9, 1);
        int maxInt = list.stream()
                .max(Integer::compareTo)
                .get();
        assertEquals(maxInt, 9);

类名::new

Person newPerson = Person.createPerson(Person::new);

  欢迎关注个人公众号:
这里写图片描述

你可能感兴趣的:(java开发)