概述
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文档接口。
网友评论