目录
1. 函数式接口概述
在之前介绍Lambda表达式的文章中提到了函数式接口,那时候只是顺带一嘴,现在来详细了解一下函数式接口的相关内容
1.1 概念与定义
有且只有一个抽象方法的接口被称为函数式接口,该接口中可以包含其他的方法(默认,静态,私有),但注意是抽象方法只有一个
假如我想定义一个函数式接口,那么有什么办法可以检查我所书写的代码防止我写错呢?还记得注解吗?这里有一个@FunctionalInterface
注解,该注解可以检测接口是否是一个函数式接口
举个例子,这里我特意写了两个抽象方法,那么该注解就会报错提醒我们没有正确定义函数式接口
1.2 使用与优化
函数式接口怎么使用呢?这里不要糊涂了,函数式接口的本质还是一个接口,你可以创建一个接口的实现类,重写其中的抽象方法
下面给出一个根据日志等级打印日志的例子,当日志等级为1的时候才打印日志
显然当日志等级是2(即如图代码)的时候是不打印的,但是细心观察一下,即使他没有打印,str1和str2字符串有没有拼接了呢?其实是有的,是拼接好再传到showLog
方法中,那么既然等级不符合就没有打印,那就意味着这个字符串白拼接了对不?那就会存在程序的性能浪费
那怎么解决这个问题呢?Lambda表达式他有一个延迟加载的特点,但其使用前提是必须存在函数式接口,因此我们来创建一个函数式接口
再使用一下这个函数式接口
这样子即使在调用showLog
方法时候已经把str1
和str2
传进去了,但是还是没有拼接,等待等级判断通过后再执行,这就是Lambda表达式的延迟加载特性,有同学可能会觉得这不是把简单的事情复杂化吗?其实从某种意义上来说确实是这样,但它也确实可以起到避免性能浪费的作用,弱化了new对象的操作,节省了内存,在后续的常用函数式接口介绍中同样使用Lambda表达式进行介绍
2. 常用的函数式接口
在java.util.function
包中,有很多JDK内置的函数式接口,下面将介绍几个最为常用的函数式接口(注意是在JDK 1.8开始才有这个特性)
2.1 Supplier接口
public interface Supplier<T>:代表结果供应商。
该接口中只包含一个无参的方法
public T get():用来获取一个泛型参数指定类型的对象数据
Supplier< T > 接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get
方法就会生产什么类型的数据
功能很简单,理解很抽象,那怎么搞呢?来看个例子,现在需求是求一个数组的最大值,要求使用Suppiler接口作为方法产生类型,通过Lambda表达式求出int
数组中的最大值(注意泛型接口需要使用Integer包装类)
这里直接看示例代码
2.2 Consumer接口
public interface Consumer<T>:表示接受单个输入参数并且不返回结果的操作。
该接口中有一个无参的方法和一个默认方法
public void accept(T t):对给定的参数执行此操作。
public default Consumer<T> andThen(Consumer<? super T> after):返回一个组合的 Consumer ,按顺序执行该操作,然后执行 after操作。
Consumer< T > 接口被称之为消费型接口,指定接口的泛型是什么类型,那么就可以使用接口中的accept
方法消费什么类型的数据,具体怎么消费就需要自定义
而其默认方法则是可以把两个Consumer接口组合在一起对数据进行消费,谁写在前边就先消费谁,其调用格式为con1.andThen(con2).accept(s)
同样的,功能很简单,理解很抽象,那怎么搞呢?来看个例子,现在需求是有一个字符串数组中存有多条个人信息,按照“姓名:xxx,年龄:xxx。”的格式把信息打印出来,要求将打印姓名的动作作为第一个Consumer接口的示例,将打印年龄的动作作为第二个Consumer接口的示例,将两个Consumer接口拼接在一起
这里直接看示例代码
2.3 Predicate接口
public interface Predicate<T>:表示一个参数的谓词(布尔值函数)。
该接口中包含一个无参的方法和三个默认方法
public boolean test(T t):在给定的参数上评估这个谓词。
public default Predicate<T> and(Predicate<? super T> other):返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑AND。
public default Predicate<T> or(Predicate<? super T> other):返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑或。
public default Predicate<T> negate():返回表示此谓词的逻辑否定的谓词。
Predicate< T > 接口就是用来对某种数据类型的数据进行判断,接口中的test
方法就是用来制定判断规则并把结果返回
其三个默认方法其实就是相对三个逻辑——与、或、非,这个概念应该很好理解,下面直接来看个例子感受一下
现在需求是有一个数组中有多条个人信息,我需要筛选其中名字为3个字母的女生,因为需要满足两个条件,因此可以使用两个Predicate接口,并且使用and
方法连接在一起
这里直接看示例代码
下面看看运行结果
2.4 Function接口
public interface Function<T,R>:表示接受一个参数并产生结果的函数。
该接口中只包含一个无参的方法和一个默认方法
public R apply(T t):将此函数应用于给定的参数。
public default <V> Function<T,V> andThen(Function<? super R,? extends V> after):返回一个组合函数,首先将该函数应用于其输入,然后将 after函数应用于结果。
Function< T , R > 接口被用作转换数据类型的,可调用其apply
方法把T数据类型的数据转换为R数据类型的数据,但转换规则需要自定义
另外,其默认方法也是把两个Function接口连接起来,第二个Function接口会把第一个Function接口的输出结果作为输入,也可认为这是链式组合(类似方法的链式调用)
老规矩,直接看例子,这个例子的需求是把一个字符串中的个人信息中的年龄抽取出来,把该年龄转化为整形数据,然后对这个年龄进行加一,然后最终返回字符串数据
这里直接看示例代码
来看看输出结果
几个例子下来,感受到我前面所说的简单事情复杂化了吧,但总是守恒的,你代码量增加了,就有机会提高所书写程序的性能
好好体会吧,溜了溜了~