美文网首页Java8
函数式接口

函数式接口

作者: 秦老厮 | 来源:发表于2019-08-12 10:01 被阅读0次

概述

java8中新增了 @FunctionalInterface 注解表示函数式接口,用注解@FunctionalInterface标识的接口都是函数式接口,函数式接口只能有一个抽象方法,但除了抽象方法,java8还允许接口中定义默认方法(声明为default的方法)和静态方法。如果一个接口只有一个抽象方法,即便我们并没有用 @FunctionalInterface 注解标注,编译器依然会将该接口看做是函数式接口。如果我们自己定义的接口只有一个抽象方法,加不加 @FunctionalInterface 注解都可以,如果加了的话,编译器就会做函数式接口检查,不满足函数式接口的要求就会报出相应的异常,编译通不过的。

但是有一点特殊情况,就是函数式接口中如果重写了Object类中声明为public的方法,那么编译器并不认为这是一个抽象方法。因为java中所有的类都是Object的子类,我们函数式接口中重写了的Object类中声明为public的方法可以被实现函数式接口的类直接从Object类继承,所以编译器并不把重写了Object类中声明为public的方法当做抽象方法。

@FunctionalInterface
public interface MyInterface {
   void hello();
   String toString();  //重写了Object类中的toString方法
}

四大函数式接口

java8中内置了四大函数式接口,通过这四大函数式接口我们可以完成大多数的需求,如果这四大函数接口不能满足需求,java中还扩展了其他的函数式接口,并且也可以自己定义函数式接口。

1.Consumer接口

Consumer是一个消费者接口,accpet(T t)方法给定一个参数,把它消费了,不返回任何结果。Consumer函数式接口的源码如下:

@FunctionalInterface
public interface Consumer<T> {

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

示例1:

Consumer<Integer> consumer = (i) -> System.out.println(i);
       consumer.accept(666);

示例2:

public class Test01 {
   public static void main(String[] args) {
       List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
       list.forEach(i -> System.out.println(i));
   }
}

forEach()方法源码跟踪:

forEach()方法位于Iterable<T>接口中,参数是一个Consumer接口类型。

default void forEach(Consumer<? super T> action) {
   Objects.requireNonNull(action);
   for (T t : this) {
       action.accept(t);
   }
}

List<>接口继承了Collection<E>接口,而Collection<E>接口又继承了Iterable<E>接口,所以List可以使用forEach()方法遍历元素。

public interface List<E> extends Collection<E> {...}
public interface Collection<E> extends Iterable<E> {...}

2.Function接口

Function接口中的抽象方法为 apply(T t),该方法有一个参数,并返回结果。除了apply这个抽象方法,还有其他的默认方法和静态方法,源码如下:

@FunctionalInterface
public interface Function<T, R> {

   R apply(T t);

   default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
       Objects.requireNonNull(before);
       return (V v) -> apply(before.apply(v));
   }

   default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
       Objects.requireNonNull(after);
       return (T t) -> after.apply(apply(t));
   }

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

示例1:

public class FunctionTest {

   public static void main(String[] args) {
       System.out.println(compute(2,i -> i * 3));
       System.out.println(compute(2,i -> i*i));
       System.out.println(compute(2,i -> i + 3));
   }

   private static int compute(int a,Function<Integer,Integer> function){
       int result = function.apply(a);  
       return result;
   }
}
执行结果

compose和andThen方法分析:

compose方法是将两个Function组合在一起,先调用参数里给定的Function的apply方法:before.apply(v),再将参数里的Function的结果给当前的Function(也就是作为返回值的Function)的apply方法作参数:apply(before.apply(v)),将结果返回。

andThen方法也是将两个Function组合在一起,但是andThen方法正好和compose方法相反,是先调用当前的Function的apply方法:apply(t),再将当前的Function的结果给参数里的Function的apply方法作参数:after.apply(apply(t)),将结果返回。

示例2

public class FunctionTest {

   public static void main(String[] args) {

       System.out.println(compute1(2,i -> i * 3,i -> i * i));
       System.out.println(compute2(2,i -> i * 3,i -> i * i));
   }

   private static int compute1(int a,Function<Integer,Integer> function1,Function<Integer,Integer> function2){
       return function1.compose(function2).apply(a);
   }

   private static int compute2(int a,Function<Integer,Integer> function1,Function<Integer,Integer> function2){
       return function1.andThen(function2).apply(a);
   }
}
执行结果

补充:以上的Function函数式接口只能传入一个参数,并返回结果,如果要传入两个参数并返回执行结果,Function函数式接口是做不到的,那么这时可以使用通过Function接口扩展的BiFunction函数式接口,该接口有三个泛型T,U,R,前两个作为方法的输入参数类型,最后一个作为返回结果类型。

该接口源码如下:

@FunctionalInterface
public interface BiFunction<T, U, R> {

   R apply(T t, U u);

   default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
       Objects.requireNonNull(after);
       return (T t, U u) -> after.apply(apply(t, u));
    }
}

示例3:

public class FunctionTest {

   public static void main(String[] args) {
       
       System.out.println(compute3(2,3,(i,j) -> i + j));
       System.out.println(compute3(2,3,(i,j) -> i * j));
   }

   private static int compute3(int a, int b, BiFunction<Integer,Integer,Integer> biFunction){
       return biFunction.apply(a,b);
   }
}
执行结果

通过BiFunction接口的源码,我们可以知道,该接口有andThen这个默认方法,没有compose方法,那么为什么BiFunction接口没有提供compose方法呢?其实这点也是很好理解的,假设BiFunction接口可以提供compose方法,并假设定义如下:

default <V> BiFunction<V, T, R> compose(BiFunction<? super V, ? extends T,? extends U> before) {
       Objects.requireNonNull(before);
       return (V v , T t) ->apply(before.apply(v, t));
    }

依照上面我们对Funciton接口的compose方法的分析,先调用参数里给定的BiFunction的apply方法:before.apply(v,u),再将参数里的BiFunction的结果给当前的BiFunction的apply方法作参数,将结果返回。

因为BiFunction的apply方法有两个参数,而Function和BiFunction接口的apply方法都只有一个返回值,所以不论参数里给定的是Funciton类型,还是BiFunction类型,都是不满足要求的。

下面分析BiFunction的andThen方法:

BiFunction的andThen方法的返回值类型是BiFunction,参数类型是Function,这和上面的Function的andThen方法是不一样的,那么为什么BiFunction的andThen方法的的参是Function类型,而不是BiFunction类型呢?其实这点也不能理解。

andThen方法是先调用当前的BiFunction的apply方法:apply(t, u),再将当前的BiFunction的结果给参数里的Function的apply方法作参数,又因为 R apply(T t, U u),BiFunction的apply方法的返回值只有一个,所以参数里只能是Function类型,如果参数里也是BiFunction类型,那么需要BiFunction的apply方法有两个参数。

示例4:

public class FunctionTest {

   public static void main(String[] args) {
       System.out.println(compute4(2,3,(i,j) -> i + j,i -> i * i));
   }
   
   private static int compute4(int a,int b,BiFunction<Integer,Integer,Integer> biFunction,
                           Function<Integer,Integer> function){
       return biFunction.andThen(function).apply(a,b);
   }
}
执行结果

3.Predicate接口

Predicate是一个断言接口,给定一个参数,返回一个boolean值,即给定一个条件返回true或者false。其源码如下:

@FunctionalInterface
public interface Predicate<T> {
   boolean test(T t);
    //返回两个Predicate的逻辑与的结果
   default Predicate<T> and(Predicate<? super T> other) {
       Objects.requireNonNull(other);
       return (t) -> test(t) && other.test(t);
   }
    //返回predicate取反后的结果
   default Predicate<T> negate() {
       return (t) -> !test(t);
   }
    //返回两个predicate逻辑或后的结果
   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);
   }
}

示例1:

public class PredicateTest {

    public static void main(String[] args) {
        Predicate<Integer> predicate = i -> i > 5;
        System.out.println(predicate.test(4));
        System.out.println(predicate.test(6));
    }
}
执行结果

示例2:

public class PredicateTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        conditionFilter(list,i -> i % 2 == 0);
    }

    private static void conditionFilter(List<Integer> list,Predicate<Integer> predicate){
        for (Integer integer : list) {
            if(predicate.test(integer)){
                System.out.println(integer);
            }
        }
    }
}
执行结果

示例3(and方法示例):

public class PredicateTest {

   public static void main(String[] args) {
       List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
       //找出集合list中大于5并且是偶数的数
       conditionFilter1(list,i -> i > 5, i -> i % 2 == 0);
   }

   private static void conditionFilter1(List<Integer> list,Predicate<Integer> predicate1,Predicate<Integer> predicate2){
       for (Integer integer : list) {
           if(predicate1.and(predicate2).test(integer)){
               System.out.println(integer);
           }
       }
   }
}
执行结果

示例4(or方法示例):

public class PredicateTest {

   public static void main(String[] args) {
       List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
       //找出集合list中大于5或者是偶数的数
       conditionFilter2(list,i -> i > 5,i -> i % 2 == 0 );
   }

   private static void conditionFilter2(List<Integer> list,Predicate<Integer> predicate1,Predicate<Integer> predicate2){
       for (Integer integer : list) {
           if(predicate1.or(predicate2).test(integer)){
               System.out.println(integer);
           }
       }
   }
}
执行结果

示例5(negate方法示例):

public class PredicateTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        //找出集合list中的所有奇数
        conditionFilter3(list,i -> i % 2 == 0);
    }

    private static void conditionFilter3(List<Integer> list,Predicate<Integer> predicate){
        for (Integer integer : list) {
            if(predicate.negate().test(integer)){
                System.out.println(integer);
            }
        }
    }
}
执行结果
4.Supplier接口

Supplier是一个提供者接口,不接受任何参数,同时返回一个结果,其源码如下:

@FunctionalInterface
public interface Supplier<T> {

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

示例:

public class Teacher {
    private String tName = "zhangkai";

    public String gettName() {
        return tName;
    }

    public void settName(String tName) {
        this.tName = tName;
    }
}
public class SupplierTest {

    public static void main(String[] args) {
        Supplier<Teacher> supplier = () -> new Teacher();
        System.out.println(supplier.get().gettName());
    }
}

总结

写到这里总算结束了,可能篇幅有点长,看起来心情有点不爽,当我看别人的播客文章的时候,太长了也会感觉很无聊,不想继续看下去,自己写的时候也长篇大论了,写的篇幅长的主要原因是扣的太细了,接口里的其他默认方法也分析了一下,而那些默认方法可能实际中用到的比较少,甚至几乎就用不到,这么做也主要是想对函数式接口了解更多一点,不至于只知道函数式接口中常用的抽象方法,也是为了我个人以后哪天看看自己写的播客复习一下,人的遗忘真的是太快了,尤其是计算机行业,要学好多东西,虽学的多,但如果没有输出,没有笔记的话,真的过段时间就一点都没了,忘得很干净,提起来只有一个印象,具体的用法以及细节已经想不起来了,这时自己的播客文章或笔记看一遍,就又都拾起来了。如果读者觉得没必要看那么细,也许我写的还有些复杂,有些废话连篇,那么不常用的那些默认方法可以不看,直接跳过即可。当然我也不能面面具到,由着四大函数式接口可以扩展出其他很多的函数式接口,比如出入两个参数的BiConsumer,BiPredicate等等,用到的时候大家可以查下jdk的api文档接口。

相关文章

  • java基础-day23-函数式接口和Stream流

    函数式接口和Stream 1. 函数式接口 1.1 函数式接口概述 1.2 常用函数式接口 1.3 比较器函数式接...

  • 2020-07-04【函数式接口】

    函数式接口概述 函数式接口作为方法的参数 函数式接口作为方法的返回值 常见的函数式接口 Supplier接口 Co...

  • 12.函数式接口

    主要内容 自定义函数式接口 函数式编程 常用函数式接口 第一章 函数式接口 1.1 概念 函数式接口在Java中是...

  • 函数式接口

    函数式接口 一、函数式接口的定义   函数式接口:函数式接口中有且仅有一个抽象方法,这个抽象方法的意义在于表达某种...

  • 测验:函数式接口

    下面哪些接口是函数式接口? 答案:只有Adder是函数式接口。SmartAdder不是函数式接口,因为它定义了两个...

  • Java8系列:神奇的函数式接口

    01 函数式接口是什么? 有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambd...

  • 函数式接口和Lambda表达式深入理解

    0x00 函数式接口 前面讲了一下函数式接口,不过可能只是讲了个大概,大致讲了一下什么是函数式接口 函数式接口就是...

  • java8函数式接口

    一、函数式接口 1、函数式接口在java中是指:有且仅有一个抽象方法的接口,函数式接口,即适用于函数式编程场景的接...

  • Java 8 知多少

    一、函数式接口 函数式接口的定义: 函数式接口(Functional Interface)就是一个有且仅有一个抽象...

  • Java lambda表达式

    1. Java函数式接口 Java实现函数式编程的方式是函数式接口(functional interface),函...

网友评论

    本文标题:函数式接口

    本文链接:https://www.haomeiwen.com/subject/fgryjctx.html