「Java8系列」神奇的函数式接口

前言

在上一篇Lambda的讲解中咱们就提到过函数式接口,好比:Consumer<String> consumer = (s) -> System.out.println(s);其中Consumer就是一个函数式接口。这里是经过Lambda表达式建立了一个函数式接口的对象。若是不知道什么是Lambda,请看《神秘的Lambda》。java

函数式接口是什么?

有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,可使用Lambda表达式建立一个函数式接口的对象,必定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。编程

@FunctionalInterface注解

与@Override 注解的做用相似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface 。该注解可用于一个接口的定义上,一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,不然将会报错。可是这个注解不是必须的,只要符合函数式接口的定义,那么这个接口就是函数式接口。bash

static方法和default方法

实在不知道该在哪介绍这两个方法了,因此就穿插在这里了。app

static方法:

java8中为接口新增了一项功能,定义一个或者多个静态方法。用法和普通的static方法同样,例如:ide

public interface Interface {
    /**
     * 静态方法
     */
    static void staticMethod() {
        System.out.println("static method");
    }
}
复制代码

注意:实现接口的类或者子接口不会继承接口中的静态方法。函数式编程

default方法:

java8在接口中新增default方法,是为了在现有的类库中中新增功能而不影响他们的实现类,试想一下,若是不增长默认实现的话,接口的全部实现类都要实现一遍这个方法,这会出现兼容性问题,若是定义了默认实现的话,那么实现类直接调用就能够了,并不须要实现这个方法。default方法怎么定义?函数

public interface Interface {
    /**
     * default方法
     */
    default void print() {
        System.out.println("hello default");
    }
}
复制代码

注意:若是接口中的默认方法不能知足某个实现类须要,那么实现类能够覆盖默认方法。不用加default关键字,例如:post

public class InterfaceImpl implements Interface {
    @Override
    public  void print() {
        System.out.println("hello default 2");
    }
}
复制代码

在函数式接口的定义中是只容许有一个抽象方法,可是能够有多个static方法和default方法。学习

自定义函数式接口

按照下面的格式定义,你也能写出函数式接口:测试

@FunctionalInterface
 修饰符 interface 接口名称 {
    返回值类型 方法名称(可选参数信息);
    // 其余非抽象方法内容
 }
复制代码

虽然@FunctionalInterface注解不是必须的,可是自定义函数式接口最好仍是都加上,一是养成良好的编程习惯,二是防止他人修改,一看到这个注解就知道是函数式接口,避免他人往接口内添加抽象方法形成没必要要的麻烦。

@FunctionalInterface
public interface MyFunction {
    void print(String s);
}
复制代码

看上图是我自定义的一个函数式接口,那么这个接口的做用是什么呢?就是输出一串字符串,属于消费型接口,是模仿Consumer接口写的,只不过这个没有使用泛型,而是将参数具体类型化了,不知道Consumer不要紧,下面会介绍到,其实java8中提供了不少经常使用的函数式接口,Consumer就是其中之一,通常状况下都不须要本身定义,直接使用就行了。那么怎么使用这个自定义的函数式接口呢?咱们能够用函数式接口做为参数,调用时传递Lambda表达式。若是一个方法的参数是Lambda,那么这个参数的类型必定是函数式接口。例如:

public class MyFunctionTest {
    public static void main(String[] args) {
        String text = "试试自定义函数好使不";
        printString(text, System.out::print);
    }

    private static void printString(String text, MyFunction myFunction) {
        myFunction.print(text);
    }
}
复制代码

执行之后就会输出“试试自定义函数好使不”这句话,若是某天需求变了,我不想输出这句话了,想输出别的,那么直接替换text就行了。函数式编程是没有反作用的,最大的好处就是函数的内部是无状态的,既输入肯定输出就肯定。函数式编程还有更多好玩的套路,这就须要靠你们本身探索了。😝

经常使用函数式接口

Consumer<T>:消费型接口

抽象方法: void accept(T t),接收一个参数进行消费,但无需返回结果。

使用方式:

Consumer consumer = System.out::println;
  consumer.accept("hello function");
复制代码

默认方法: andThen(Consumer<? super T> after),先消费而后在消费,先执行调用andThen接口的accept方法,而后在执行andThen方法参数after中的accept方法。

使用方式:

Consumer<String> consumer1 = s -> System.out.print("车名:"+s.split(",")[0]);
  Consumer<String> consumer2 = s -> System.out.println("-->颜色:"+s.split(",")[1]);

  String[] strings = {"保时捷,白色", "法拉利,红色"};
  for (String string : strings) {
     consumer1.andThen(consumer2).accept(string);
  }
复制代码

输出: 车名:保时捷-->颜色:白色 车名:法拉利-->颜色:红色

Supplier<T>: 供给型接口

抽象方法:T get(),无参数,有返回值。

使用方式:

Supplier<String> supplier = () -> "我要变的颇有钱";
 System.out.println(supplier.get());
复制代码

最后输出就是“我要变得颇有钱”,这类接口适合提供数据的场景。

Function<T,R>: 函数型接口

抽象方法: R apply(T t),传入一个参数,返回想要的结果。

使用方式:

Function<Integer, Integer> function1 = e -> e * 6;
 System.out.println(function1.apply(2));
复制代码

很简单的一个乘法例子,显然最后输出是12。

默认方法:

  • compose(Function<? super V, ? extends T> before),先执行compose方法参数before中的apply方法,而后将执行结果传递给调用compose函数中的apply方法在执行。

使用方式:

Function<Integer, Integer> function1 = e -> e * 2;
 Function<Integer, Integer> function2 = e -> e * e;

 Integer apply2 = function1.compose(function2).apply(3);
 System.out.println(apply2);
复制代码

仍是举一个乘法的例子,compose方法执行流程是先执行function2的表达式也就是33=9,而后在将执行结果传给function1的表达式也就是92=18,因此最终的结果是18。

  • andThen(Function<? super R, ? extends V> after),先执行调用andThen函数的apply方法,而后在将执行结果传递给andThen方法after参数中的apply方法在执行。它和compose方法整好是相反的执行顺序。

使用方式:

Function<Integer, Integer> function1 = e -> e * 2;
 Function<Integer, Integer> function2 = e -> e * e;

 Integer apply3 = function1.andThen(function2).apply(3);
 System.out.println(apply3);
复制代码

这里咱们和compose方法使用一个例子,因此是如出一辙的例子,因为方法的不一样,执行顺序也就不相同,那么结果是大大不一样的。andThen方法是先执行function1表达式,也就是32=6,而后在执行function2表达式也就是66=36。结果就是36。 **静态方法:**identity(),获取一个输入参数和返回结果相同的Function实例。

使用方式:

Function<Integer, Integer> identity = Function.identity();
 Integer apply = identity.apply(3);
 System.out.println(apply);
复制代码

日常没有遇到过使用这个方法的场景,总之这个方法的做用就是输入什么返回结果就是什么。

Predicate<T> : 断言型接口

抽象方法: boolean test(T t),传入一个参数,返回一个布尔值。

使用方式:

Predicate<Integer> predicate = t -> t > 0;
 boolean test = predicate.test(1);
 System.out.println(test);
复制代码

当predicate函数调用test方法的时候,就会执行拿test方法的参数进行t -> t > 0的条件判断,1确定是大于0的,最终结果为true。

默认方法:

  • and(Predicate<? super T> other),至关于逻辑运算符中的&&,当两个Predicate函数的返回结果都为true时才返回true。

使用方式:

Predicate<String> predicate1 = s -> s.length() > 0;
 Predicate<String> predicate2 = Objects::nonNull;
 boolean test = predicate1.and(predicate2).test("&&测试");
 System.out.println(test);
复制代码
  • or(Predicate<? super T> other) ,至关于逻辑运算符中的||,当两个Predicate函数的返回结果有一个为true则返回true,不然返回false。

使用方式:

Predicate<String> predicate1 = s -> false;
 Predicate<String> predicate2 = Objects::nonNull;
 boolean test = predicate1.and(predicate2).test("||测试");
 System.out.println(test);
复制代码
  • negate(),这个方法的意思就是取反。

使用方式:

Predicate<String> predicate = s -> s.length() > 0;
 boolean result = predicate.negate().test("取反");
 System.out.println(result);
复制代码

很明显正常执行test方法的话应该为true,可是调用negate方法后就返回为false了。 **静态方法:**isEqual(Object targetRef),对当前操做进行"="操做,即取等操做,能够理解为 A == B。

使用方式:

boolean test1 = Predicate.isEqual("test").test("test");
 boolean test2 = Predicate.isEqual("test").test("equal");
 System.out.println(test1);   //true
 System.out.println(test2);   //false
复制代码

其余函数式接口

Bi类型接口

BiConsumer、BiFunction、BiPrediate 是 Consumer、Function、Predicate 的扩展,能够传入多个参数,没有 BiSupplier 是由于 Supplier 没有入参。

操做基本数据类型的接口

IntConsumer、IntFunction、IntPredicate、IntSupplier、LongConsumer、LongFunction、LongPredicate、LongSupplier、DoubleConsumer、DoubleFunction、DoublePredicate、DoubleSupplier。 其实经常使用的函数式接口就那四大接口Consumer、Function、Prediate、Supplier,其余的函数式接口就不一一列举了,有兴趣的能够去java.util.function这个包下详细的看。

你们看后辛苦点个赞点个关注哦!后续还会后更多的博客。想来想去建了个群,你们能够扫码加群,一块儿学习、共同进步。有兴趣能够扫码加群。若有错误,烦请指正。

相关文章
相关标签/搜索