查看原文
其他

JDK8新特性之函数式接口

2017-10-03 javastack Java技术栈


什么是函数式接口

先来看看传统的创建线程是怎么写的

  1. Thread t1 = new Thread(new Runnable() {

  2.    @Override

  3.    public void run() {

  4.        System.out.println("t1");

  5.    }

  6. });

  7. t1.start();

再来看看使用了函数式接口是怎么写的

  1. Thread t2 = new Thread(() -> System.out.println("t2"));

  2. t2.start();

Runnable接口直接可以使用Lambda表达式来编写,这是因为Runnable接口是一个函数式接口,来看看Runnable的源码。

  1. @FunctionalInterface

  2. public interface Runnable {

  3.    public abstract void run();

  4. }

发现该接口加上了函数式接口的定义注解: @FunctionalInterface,表明该接口是一个函数式接口。

  1. @Documented

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Target(ElementType.TYPE)

  4. public @interface FunctionalInterface {

  5. }

在JDK8中,除了Runnbale接口,还有像Comparator、Callable等接口都加上了该注解定义为函数式接口。

内置函数式接口

JDK8提供了几个内置的函数式接口,用在了许多API的地方,都可以拿来用,可以满足大部分应用。

  1. //Consumer<T> - T作为输入,执行某种动作但没有返回值

  2. Consumer<String> con = (x) -> {

  3.    System.out.println(x);

  4. };

  5. con.accept("hello world");

  6. //Supplier<T> - 没有任何输入,返回T

  7. Supplier<String> supp = () -> {

  8.    return "Supplier";

  9. };

  10. System.out.println(supp.get());

  11. //Predicate<T> -T作为输入,返回的boolean值作为输出

  12. Predicate<String> pre = (x) -> {

  13.    System.out.print(x);

  14.    return x.startsWith("op");

  15. };

  16. System.out.println(": " + pre.test("op, hello World"));

  17. // Function<T, R> -T作为输入,返回的R作为输出

  18. Function<String, String> function = (x) -> {

  19.    System.out.print(x + ": ");

  20.    return "Function";

  21. };

  22. System.out.println(function.apply("hello world"));

  23. //BinaryOperator<T> -两个T作为输入,返回一个T作为输出,对于“reduce”操作很有用

  24. BinaryOperator<String> bina = (x, y) -> {

  25.    System.out.print(x + " " + y);

  26.    return "BinaryOperator";

  27. };

  28. System.out.println("  " + bina.apply("hello ", "world"));

自定义函数式接口

1、自定义一个函数式接口

  1. @FunctionalInterface

  2. public interface CalcInterface<N, V> {    

  3.    V operation(N n1, N n2);

  4. }

这里只有一个抽象方法,@FunctionalInterface注解可以不用写,至于为什么可以往下看。

2、新建一个引用函数式接口的类

  1. public static class NumberOperation<N extends Number, V extends Number> {

  2.    private N n1;

  3.    private N n2;

  4.    public NumberOperation(N n1, N n2) {

  5.        this.n1 = n1;

  6.        this.n2 = n2;

  7.    }

  8.    public V calc(CalcInterface<N, V> ci) {

  9.        V v = ci.operation(n1, n2);

  10.        return v;

  11.    }

  12. }

3、测试函数式接口

  1. private static void testOperationFnInterface() {

  2.        NumberOperation<Integer, Integer> np = new NumberOperation(13, 10);

  3.    CalcInterface<Integer, Integer> addOper1 = (n1, n2) -> {

  4.        return n1 + n2;

  5.    };

  6.    CalcInterface<Integer, Integer> multiOper1 = (n1, n2) -> {

  7.        return n1 * n2;

  8.    };

  9.    System.out.println(np.calc1(addOper1));

  10.    System.out.println(np.calc1(multiOper1));

  11.    // 上面的可以简写为

  12.    System.out.println(np.calc1((n1, n2) -> n1 + n2));

  13.    System.out.println(np.calc1((n1, n2) -> n1 * n2));

  14. }

最后输出:

  1. 23

  2. 130

  3. 23

  4. 130

函数式接口规范

1、@FunctionalInterface标识为一个函数式接口只能用在只有一个抽象方法的接口上。

2、接口中的静态方法、默认方法、覆盖了Object类的方法都不算抽象方法。

3、@FunctionalInterface注解不是必须的,如果该接口只有一个抽象方法可以不写,它默认就符合函数式接口,但建议都写上该注解,编译器会检查该接口是否符合函数式接口的规范。

举例说明

正确的函数式接口。

  1. @FunctionalInterface

  2. public interface CalcInterface<N, V> {    

  3.    V operation(N n1, N n2);

  4. }

加了几个符合函数式的方法也没事,编译器也不会报错。

  1. @FunctionalInterface

  2. public interface CalcInterface<N, V> {        

  3.    V operation(N n1, N n2);

  4.    public boolean equals(Object object);

  5.    public default void defaultMethod() {

  6.    }

  7.    public static void staticMethod() {

  8.    }

  9. }

这个没用@FunctionalInterface函数式接口,有两个抽象方法,不能用于Lambda表达式。

  1. public interface CalcInterface<N, V> {    

  2.    V operation(N n1, N n2);

  3.    V operation2(N n1, N n2);

  4. }

这个有两个抽象方法的用@FunctionalInterface注解的函数式接口编译会报错。

  1. @FunctionalInterface

  2. public interface CalcInterface<N, V> {    

  3.    V operation(N n1, N n2);

  4.    V operation2(N n1, N n2);

  5. }

这个没有一个抽象方法,编译报错。

  1. public interface CalcInterface<N, V> {    

  2. }

推荐阅读



什么是Spring Boot?

Spring Boot开启的2种方式

Spring Boot Starters启动器

Spring Boot定制启动图案

Spring Boot核心配置

Spring Boot功能实战

Spring Boot自动配置原理、实战

Spring Boot Runner启动器

Spring Boot - Profile不同环境配置


看完有没有收获?

分享到朋友圈给更多的人吧。




  Java技术栈  
微信公众号:「Javastack

分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。


 ▼长按二维码关注我们↓↓↓




    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存