函数式接口

概述

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文档接口。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容