您的位置:首页 >聚焦 >

JDK1.8特性之函数式接口

2022-03-06 09:24:04    来源:程序员客栈
前言

我们都知道,从JDK1.8开始,java已经对函数式编程有了比较完善的支持,除了我们常用的stream之外,还有Optional、Supplier、Function等,其中Function就是函数式接口的一种方式,函数式编程极大提升了我们的编程效率和代码逻辑简洁度,今天我们就抽点时间来看下@FunctionalInterface(函数式接口)的简单应用。

@FunctionalInterface

虽然@FunctionalInterface和其他的lambda一样,都是jdk1.8引入的,但相比于stream和Optional这两种常用的新特性,我对@FunctionalInterface的了解就显得很浅薄了。

哪怕是经常在stram()中用到它,但对于具体如何使用它,如何更好地应用它,我还真的是一知半解。

下面我们先通过一个简单的实例,来见识下@FunctionalInterface的魅力:

一个简单场景

假设我们有一个方法,需要在方法中的某个执行结果为success的时候执行一段代码或者调另一个方法,在执行结果为failed的时候执行另一段代码,在传统实现下,我们需要这样操作:

publicvoiddealThing(Stringparameter){//执行业务Stringresult="123".equals(parameter)?"success":"failed";if("success".equals(parameter)){System.out.println("业务1执行成功");}if("failed".equals(parameter)){System.out.println("业务1执行失败");}}

当然这样写没有任何问题,但是如果我们执行结果的逻辑还需要在其他方法中执行,而且其中成功和失败的业务处理还不尽相同,这样在传统操作中,我们要么需要多加一层业务判断,要么重新写个方法,不过这两种方式都增加了冗余代码:

//增加逻辑处理publicvoiddealThing(Stringparameter){if("业务1".equals(parameter)){//执行业务Stringresult="123".equals(parameter)?"success":"failed";if("success".equals(result)){System.out.println("业务1执行成功");}if("failed".equals(result)){System.out.println("业务1执行失败");}}else{//执行业务Stringresult="124".equals(parameter)?"success":"failed";if("success".equals(result)){System.out.println("业务2执行成功");}if("failed".equals(result)){System.out.println("业务2执行失败");}}}//或者重写方法publicvoiddealThing2(Stringparameter){//执行业务Stringresult="124".equals(parameter)?"success":"failed";if("success".equals(parameter)){System.out.println("业务2执行成功");}if("failed".equals(parameter)){System.out.println("业务2执行失败");}}

当然,如果是一两个业务,我觉得都还可以接受,但如果是十几个或者几十个业务的时候,这种处理方式就显得很不友好,而且往往success和failed需要处理的业务逻辑可能不尽相同,所以我们还需要更友好更能够简化我们业务实现逻辑的方法,这时候就到了函数式接口发光发热的时候了:

函数式接口应用

如果采用函数式接口的方式,对于上面这样的需求场景,我们只需要增加两个Consumer入参即可——成功回调、失败回调:

/***结果回调*@paramresult*@paramsuccessFun*@paramfailedFun*/publicvoiddealThing(Stringparameter,ConsumersuccessFun,ConsumerfailedFun){//执行业务Stringresult="124".equals(parameter)?"success":"failed";if("success".equals(result)){successFun.accept(result);}if("failed".equals(result)){failedFun.accept(result);}}

这里的Consumer就是函数式接口的一种,使用也很简单,就是在调用具体方法的时候,要提供接口的具体实现,也就是我们上面的successFun、failedFun入参,具体调用实例如下:

Stringparameter="123";newFunctionalInterfaceTest().dealThing(parameter,s->{System.out.println("业务执行成功"+s);},f->{System.out.println("业务执行失败"+f);});

运行结果如下:

Consumer函数式接口

下面我们来简单看下Consumer接口的基本结构:

和我们通常定义的接口有所不同的是,函数式接口有一个@FunctionalInterface注解,其中的accept方法就是我们在调用方法时需要实现的方法。除了Consumer函数式接口之外,java还为我们提供了其他很多有用的函数式接口,比如Function,与Consumer不同的是,Function提供的函数式接口是可以有返回值的:

定义自己的函数式接口

关于官方提供的函数式接口我们就先看这么多,下面我们一起来看下如何定制自己的函数式接口,其实也很简单:

首先是定义接口和接口方法,然后加上@FunctionalInterface,这样一个函数式接口就定义好了:

@FunctionalInterfacepublicinterfaceMyFunctionInterface{voidsyske(Tt);}

使用和我们上面演示的用法没有任何区别:

newFunctionalInterfaceTest().functionTest("syske","hello",parameter->System.out.println("syske,"+parameter));

当然除了上面我们演示的这种方式之外,我们还可以通过下面这种方式来使用函数式接口:

publicvoidsyskeFun(Stringparameter){System.out.println("syske,"+parameter);}//当前类的非静态方法调用newFunctionalInterfaceTest().functionTest("syske","hello",this::syskeFun);//静态方法调用,在这种情况下,需要把syskeFun定义为静态方法newFunctionalInterfaceTest().functionTest("syske","hello",FunctionalInterfaceTest::syskeFun);

需要注意的是,函数式接口中只能定义一个未实现的接口,否则会报编译错误:

多参数函数式接口

上面我们演示的都是单参数的函数式接口,其实官方也提供了很多其他的函数式接口,比如带返回值的函数式接口Function、多参数的接口BiFunction

这里的BiFunction有两个入参和一个返回值,如果两个参数依然满足不了你的需要,你还可以自己定义多参数接口,这里我定义的函数式接口有5个参数:

@FunctionalInterfacepublicinterfaceMyFunctionInterface2{voidsyske(Tt,Rr,Uu,Xx,Yy);}

调用方式也是大同小异:

publicvoidsetMyFunctionInterface2(Stringt,Stringr,Stringu,Stringx,Stringy,MyFunctionInterface2interface2){interface2.syske(t,r,u,x,y);}newFunctionalInterfaceTest().setMyFunctionInterface2("parameter1","parameter2","parameter3","parameter4","parameter5",(t,r,u,x,y)->System.out.printf("%s.%s.%s.%s.%s",t,r,u,x,y));

结语

相比于传统的方法调用,函数式接口的入参更灵活也更方便,让我们可以在不同的业务中使用不同的逻辑实现方式,可以极大简化代码中的if-else逻辑,甚至在某些场景下,还可以代替策略模式。好了,关于函数式接口的简单介绍和应用我们就先分享这么多,感兴趣的小伙伴可以亲自动手试试,当然最好的实践就是在实际应用开发中使用它,并让它为我们的开发工作带来便利。

- END -

关键词: 静态方法 下面我们 不尽相同

相关阅读