Java 函数式编程(常用接口)

        之前已经介绍过了Java8函数式变成及Lambda表达式,感兴趣可以看看,地址:Java8函数式编程(Lambda表达式)_琅琊之榜PJ的博客-CSDN博客

 本文主要介绍一下常用的接口及用法,先来看一个表格:

Java 函数式编程(常用接口)_第1张图片

 本文主要选取几个常用的:Supplier、Consumer、Function、Predicate

在此之前,再来了解一下@FunctionalInterface注解。

一、@FunctionalInterface注解

        一种信息注释类型,用于指示接口类型声明是Java语言规范定义的函数接口。从概念上讲,函数接口只有一个抽象方法。由于默认方法有一个实现,所以它们不是抽象的。如果一个接口声明了一个覆盖java.lang.Object的一个公共方法的抽象方法,那么这也不计入该接口的抽象方法计数,因为该接口的任何实现都将具有来自java.lang.Oobject或其他地方的实现。

        注意,函数接口的实例可以使用lambda表达式、方法引用或构造函数引用创建。

        如果使用此注解类型对类进行注解,则编译器需要生成错误消息,除非类型是接口类型,而不是注解类型、枚举或类。

        带注解的类型满足功能接口的要求。

        但是,无论接口声明中是否存在FunctionalInterface注释,编译器都会将符合函数接口定义的任何接口视为函数接口。

源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

需要注意的是:函数式接口只有一个抽象方法,接口声明覆盖 java.lang.Object的抽象方法,不会进行抽象方法计数。这对后面介绍几个常用的接口很有用!!

二、Supplier

        先看一下源码:

/**
  * 表示结果的供应者。
  * 没有要求每次调用供应者时都返回新的或不同的结果。
  */
@FunctionalInterface
public interface Supplier {

    /**
     * Gets a result.
     */
    T get();
}

        Supplier 接口源码中只有个get()方法。每次调用get()方法,就会调用构造方法,获取对象实例。

        示例:每次调用get()方法都返回一个整数:

        Supplier supplier = new Supplier() {
            @Override
            public Integer get() {
                return new Random().nextInt();
            }
        };
        System.out.println(supplier.get());

        结合Lambda表达式应用:

    List integers = makeList(10, () -> (int) (Math.random() * 10));
    integers.forEach(System.out::println);
 
    private List makeList(int num, Supplier supplier){
        List list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            list.add(supplier.get());
        }
        return list;
    }

        结合方法引用应用:

Supplier supplier1 = Math::random;
System.out.println(supplier1.get());

三、Consumer

Java源码:

@FunctionalInterface
public interface Consumer {

    void accept(T t);

    default Consumer andThen(Consumer after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

        Consumer表示接受单个输入参数而不返回结果的操作。java.util.function.Consumer 接口则正好与上面的Supplier 接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定。

该类有两个方法

  • void accept(T t);【抽象方法】

        该方法对给定的参数执行此操作。

  • default Consumer andThen(Consumer after)【默认实现方法】

        返回一个组合的Consumer,该Consumer依次执行此操作和after操作。如果执行任何一个操作都会引发异常,则会将异常中继到组合操作的调用方。如果执行此操作会引发异常,则不会执行after操作。

1、抽象方法accept的使用

该方法消费一个指定泛型的数据:

import java.util.function.Consumer;
 
public class Demo01Consumer {
    public static void main(String[] args) {
        consumerString(s -> System.out.println(s));
    }
 
    private static void consumerString(Consumer function) {
        function.accept("Hello");
    }
}

运行程序,控制台输出:Hello

2、默认方法andThen的使用

        如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作, 然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。

default Consumer andThen(Consumer after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
}

提示:java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出 NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。

        要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合的情况:

import java.util.function.Consumer;
 
public class Demo02Consumer {
    public static void main(String[] args) {
        consumerString(
                // toUpperCase()方法,将字符串转换为大写
                s -> System.out.println(s.toUpperCase()),
                // toLowerCase()方法,将字符串转换为小写
                s -> System.out.println(s.toLowerCase())
        );
    }
 
    private static void consumerString(Consumer one, Consumer two) {
        one.andThen(two).accept("Hello");
    }
}

        运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。

四、Function

源码如下:

@FunctionalInterface
public interface Function {


    R apply(T t);


    default  Function compose(Function before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }


    default  Function andThen(Function after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static  Function identity() {
        return t -> t;
    }
}

Function接口的作用的是用来表现一个参数类型为T、返回值类型为R的函数(方法)。

Function 接口是一个功能型接口,是一个转换数据的作用。接收一个T参数,返回一个R结果

Function 接口实现 apply 方法来做转换。

一个简单的示例:

Function function = p -> p.length() == 5;
Stream stream = stringList().stream().map(function);

默认方法1 Function compose(Function before)

返回一个组合函数,该函数首先将before函数应用于其输入,然后将此函数应用于结果。如果对任意一个函数的求值引发异常,则会将异常中继到组合函数的调用方。

默认方法2 Function andThen(Function after)

返回一个组合函数,该函数首先将此函数应用于其输入,然后将after函数应用于结果。如果对任意一个函数的求值引发异常,则会将异常中继到组合函数的调用方。

静态方法:static Function identity()

返回一个始终返回其输入参数的函数。

五、Predicate

源码:

@FunctionalInterface
public interface Predicate {


    boolean test(T t);

    default Predicate and(Predicate other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate negate() {
        return (t) -> !test(t);
    }

    default Predicate or(Predicate other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }


    static  Predicate isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

Predicate常用的四个方法:
boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
default Predicate negate():返回一个逻辑的否定,对应逻辑非
default Predicate and(Predicate other):返回一个组合判断,对应短路与
default Predicate or(Predicate other):返回一个组合判断,对应短路或
Predicate接口常用于判断参数是否满足指定的条件

最常见的应用是在集合Stream流过滤条件中使用:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
 
 
public class LambdaStudy
{
    public static void main(String[] args) {
       
        //初始化list集合
        List list = new ArrayList();
        list.add("测试数据1");
        list.add("测试数据2");
        list.add("测试数据3");
        list.add("测试数据12");
        
        //使用λ表达式遍历集合
        list.forEach(s -> System.out.println(s));
        
        //结合Predicate使用和过滤条件筛选元素
        Predicate contain1 = n -> n.contains("1");
        Predicate contain2 = n -> n.contains("2");
        
        //根据条件遍历集合
        list.stream().filter(contain1).forEach(n -> System.out.println(n));
        list.stream().filter(s -> contain1.test(s)).forEach(s -> System.out.println(s));
        list.stream().filter(contain1.and(contain2)).forEach(n -> System.out.println(n));
        list.stream().filter(contain1.or(contain2)).forEach(n -> System.out.println(n));
        
        //将过滤后的元素重新放到一个集合中
        List newList = list.stream().filter(contain1.and(contain2)).collect(Collectors.toList());
        
        newList.forEach(s -> System.out.println(s));
 
    }
}

你可能感兴趣的:(Java,java,开发语言,函数式编程)