Java8新特性之常用的函数式接口----Function,Consumer,Predicate,Supplier

一.什么是函数式接口

最近在开发过程中,发现许多Java8新特性的应用及其广泛,很多代码如果不了解这些知识是看不懂的,所以打算总结一下。


  • 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
  • 函数式接口可以被隐式转换为 lambda 表达式

lambda表达式介绍: lambda表达式介绍

代码示例:

@FunctionalInterface
interface GreetingService 
{
    void sayMessage(String message);
}

目前个人理解:函数式接口其实就是一种接口而已,为什么它如此特殊是因为它只有一个抽象方法,方便我们用Lambda表达式等方式来写代码

比如上面的接口可以这样使用:

GreetingService greetService1 = message -> System.out.println("Hello " + message);


二.四大函数式接口

java.util.function 它包含了很多类,用来支持 Java的 函数式编程,其中有许多函数式接口,但是比较基本比较常用的有四种,分别是Function,Consumer,Predicate,Supplier。下面就让我们一起来学习一下

附常见的泛型类型:

使用大写字母A,B,C,D......X,Y,Z定义的,就都是泛型,把T换成A也一样,这里T只是名字上的意义而已
 
? 表示不确定的java类型
T (type) 表示具体的一个java类型
K V (key value) 分别代表java键值中的Key Value
E (element) 代表Element

1.Function功能型函数式接口

表示接受一个参数并产生结果的函数

@FunctionalInterface
public interface Function<T, R> {

    /**
     * 接收一个参数,进行操作后将结果返回
     */
    R apply(T t);

    /**
     * 返回一个 先执行before函数对象apply方法,再执行当前函数对象apply方法的 函数对象
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * 返回一个 先执行当前函数对象apply方法, 再执行after函数对象apply方法的 函数对象
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     * 返回一个执行了apply()方法之后只会返回输入参数的函数对象
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

可以看出来Function接口的核心就是apply这个方法。

代码示例:

public class FunctionDemo {

    static int calValue(int value, Function<Integer, Integer> function) {
        return function.apply(value);
    }


    public static void main(String[] args) {

        int value = 10;
        //lambda表达式
        int res = calValue(value, (x) -> {return value + 10;});
        System.out.println(res);
    }
}

calValue作为类的静态方法,传入一个Function接口作为参数,返回值是经过函数计算的结果。在main方法中用lambda表达式实现了这个接口,这样让函数的扩展性更强了。


2.Predicate断言型函数式接口

接受一个输入参数,返回一个布尔值结果。

@FunctionalInterface
public interface Predicate<T> {

    /**
     * 评估当前参数
     */
    boolean test(T t);

    /**
     * 对应了java的连接符号&&
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * 对应了java的连接符号!
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * 对应了java的连接符号||
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

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

除了主要的抽象方法test外,另外三个方法其实都是用于逻辑判断的,可以让我们对断言进行排列组合,方便编程

int[] numbers= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        List<Integer> list=new ArrayList<>();
        for(int i:numbers) {
            list.add(i);
        }
        Predicate<Integer> p1=i->i>5;
        Predicate<Integer> p2=i->i<20;
        Predicate<Integer> p3=i->i%2==0;
        List test=list.stream().filter(p1.and(p2).and(p3)).collect(Collectors.toList());
        System.out.println(test.toString());
/** print:[6, 8, 10, 12, 14]*/

需求是输出偶数,只需要通过stream流式编程然后定义三个Predicate进行过滤即可。


3.Consumer消费型函数式接口

代表了 接受一个输入参数并且无返回的操作

    public static void modifyTheValue(int value, Consumer<Integer> consumer) {
        consumer.accept(value);
    }
v
    public static void main(String[] args) {
        // (x) -> System.out.println(x * 2)接受一个输入参数x
        // 直接输出,并没有返回结果
        // 所以该lambda表达式可以实现Consumer接口
        modifyTheValue(3, (x) -> System.out.println(x * 2));
    }

输出结果为6


4.Supplier供给型函数式接口

无参数,返回一个结果

    public static String supplierTest(Supplier<String> supplier) {  
        return supplier.get();  
    }  

    public static void main(String args[]) {
        String name = "冷冷";
        // () -> name.length() 无参数,返回一个结果(字符串长度)
        // 所以该lambda表达式可以实现Supplier接口
        System.out.println(supplierTest(() -> name.length() + ""));
    }

三.总结

函数式接口为Java赋予了函数式编程的能力。

“函数式编程"是一种"编程范式”(programming paradigm),也就是如何编写程序的方法论。它属于"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用,有许多的好处。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章