JDK8 新特性——函数式接口

为什么需要函数式接口?

在函数式编程思想下,允许把函数本身作为参数传入另一个函数。Java中使用函数式接口来实现”传递行为“的这种思想?

什么是函数式接口?

函数式接口(Functional Interface)就是有一个且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

@FunctionalInterface注解

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

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

自定义函数式接口

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

上面就是自定义函数式的接口的格式。

@FunctionalInterface
public interface MyFunction {
    void print(String s);
}

上面是自己定义的函数式接口,把么这个接口的作用是什么呢?就是输出一串字符串,属于消费型接口,是模仿Customer接口写的,只不过这个没有写泛型,而是将参数具体类型化了,不知道没关系,下面会介绍到,java8中提供了很多常用的函数式接口,Customer就是其中之一,一般情况下不需要自己定义,直接使用就好了。那么怎么使用这个自定义的函数式接口呢?我们可以用函数式接口作为参数,调用时传递Lambda表达式。如果一个方法的参数是Lambda,那么这个参数的类型一定是函数式接口。例如

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

    private static void printString(String s, MyFuntion myFuntion) {
        myFuntion.print(s);
    }
}

常用函数式接口

在jdk8中,引入了一个新的包java.util.function,可以使用java8的函数式编程变得更加简便。这个package中的接口大致分为一下四类:

Consumer<T>:消费型接口

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

使用方式

Consumer<Integer> consumer = (num) -> System.out.println(num*5);
consumer.accept(5);

默认方法

default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }

Consumer函数式接口还提供了一个andThen默认方法,该默认方法提供了自身consumer消费行为和atfter参数提供消费行为的组合。

Supplier<T>:供给型接口

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

**使用方式:**这类接口适合提供数据的场景。

Supplier<String> supplier = () -> "welcome";
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));

默认方法Function函数式接口还提供了2种组合Function函数式接口的默认方法:

  • compose默认方法提供了before参数提供的apply方法和自身apply方法的组合。将V类型参数执行before函数的apply方法后的结果作为本函数的入参,然后执行本函数的apply方法。

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
            Objects.requireNonNull(before);
            return (V v) -> apply(before.apply(v));
        }
    

使用方式

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的表达式也就是3*3=9,然后在将执行结果传给function1的表达式也就是9*2=18,所以最终的结果是18。


  • andThen默认方法提供了自身apply方法和after参数的apply方法的组合。将T类型执行自身apply方法的结果作为after参数apply方法的入参,然后再执行after参数的apply方法。

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
            Objects.requireNonNull(after);
            return (T t) -> after.apply(apply(t));
        }
    

使用方式:

        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);

由于方法的不同,执行顺序也就不相同,那么结果是大大不同的。andThen方法是先执行function1表达式,也就是3*2=6,然后在执行function2表达式也就是6*6=36。结果就是36。

静态方法

  • Function函数式接口还提供了一个静态方法identify方法用来原样返回入参的特殊Function类型函数。

    static <T> Function<T, T> identity() {
            return t -> t;
        }
    

Predicate<T>: 断言型接口

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

使用方式:

Predicate<Integer> predicate = (age) -> age > 15;
System.out.println(predicate.test(16));

默认方法:

  • and 默认方法提供了组合自身Predicate和other参数 Predicate的功能,实现自身Predicate和other参数 Predicate逻辑与的功能。
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
  • or 默认方法提供了组合自身Predicate和other参数 Predicate的功能,实现自身Predicate和other参数 Predicate逻辑或的功能。
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
  • negate 默认方法提供了将自身Predicate结果取反的功能。
    另外Predicate函数式接口还提供了一个静态isEqual方法,该isEqual方法使用targetRef参数的equals方法判断,T类型参数是否相等。
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

使用方式:

        System.out.println(Predicate.isEqual("abc").test("abc1")); // false
        System.out.println(Predicate.isEqual("abc").test("abc")); // true

其他函数式接口

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这个包下详细的看。

functionalInterface

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议