• Function源码解析与实践


    作者:陈昌浩

    1 导读

    if…else…在代码中经常使用,听说可以通过Java 8的Function接口来消灭if…else…!Function接口是什么?如果通过Function接口接口消灭if…else…呢?让我们一起来探索一下吧。

    2 Function接口

    Function接口就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,Function接口可以被隐式转换为 lambda 表达式。可以通过FunctionalInterface注解来校验Function接口的正确性。Java 8允许在接口中加入具体方法。接口中的具体方法有两种,default方法和static方法。

    @FunctionalInterface
    interface TestFunctionService
    {
        void addHttp(String url);
    }
    

    那么就可以使用Lambda表达式来表示该接口的一个实现。

    TestFunctionService testFunctionService = url -> System.out.println("http:" + url);
    

    2.1 FunctionalInterface

    2.1.1 源码
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface FunctionalInterface {}
    
    2.1.2 说明

    上图是FunctionalInterface的注解说明。通过上面的注解说明,可以知道FunctionalInterface是一个注解,用来说明一个接口是函数式接口。 函数式接口只有一个抽象方法。 可以有默认方法,因为默认方法有一个实现,所以不是抽象的。函数接口的实例可以用lambda表达式、方法引用或构造函数引用创建。

    FunctionalInterface会校验接口是否满足函数式接口:

    • 类型必须是接口类型,不能是注释类型、枚举或类。
    • 只能有一个抽象方法。
    • 可以有多个默认方法和静态方法。
    • 可以显示覆盖java.lang.Object中的抽象方法。

    编译器会将满足函数式接口定义的任何接口视为函数式接口,而不管该接口声明中是否使用FunctionalInterface注解。

    3 Function接口主要分类

    Function接口主要分类:

    • Function:Function函数的表现形式为接收一个参数,并返回一个值。
    • Supplier:Supplier的表现形式为不接受参数、只返回数据。
    • Consumer:Consumer接收一个参数,没有返回值。
    • Runnable:Runnable的表现形式为即没有参数也没有返回值。

    3.1 Function

    Function函数的表现形式为接收一个参数,并返回一个值。

    3.1.1 源码
    @FunctionalInterface
    public interface Function {
        R apply(T t);
        default  Function compose(Functionsuper V, ? extends T> before) {
            Objects.requireNonNull(before);
            return (V v) -> apply(before.apply(v));
        }
        default  Function andThen(Functionsuper R, ? extends V> after) {
            Objects.requireNonNull(after);
            return (T t) -> after.apply(apply(t));
        }
        static  Function identity() {
            return t -> t;
        }
    }
    
    3.1.2 方法说明
    • apply:抽象方法。将此函数应用于给定的参数。参数t通过具体的实现返回R。
    • compose:default方法。返回一个复合函数,首先执行fefore函数应用于输入,然后将该函数应用于结果。如果任意一个函数的求值引发异常,则将其传递给组合函数的调用者。
    • andThen:default方法。返回一个复合函数,该复合函数首先对其应用此函数它的输入,然后对结果应用after函数。如果任意一个函数的求值引发异常,则将其传递给组合函数的调用者。
    • identity:static方法。返回一个始终返回其输入参数的函数。
    3.1.3 方法举例

    1)apply

    测试代码:

    public  String upString(String str){
        Function<String, String> function1 = s -> s.toUpperCase();
        return function1.apply(str);
    }
     public static void main(String[] args) {
         System.out.println(upString("hello!"));
     }
    

    通过apply调用具体的实现。执行结果:

    2)compose

    测试代码:

    public static void main(String[] args) {
        Function<String, String> function1 = s -> s.toUpperCase();
        Function<String, String> function2 = s -> "my name is "+s;
        String result = function1.compose(function2).apply("zhangSan");
        System.out.println(result);
    }
    

    执行结果

    如结果所示:compose 先执行function2 后执行function1。

    3)andThen

    测试代码:

    public static void main(String[] args) {
        Function<String, String> function1 = s -> s.toUpperCase();
        Function<String, String> function2 = s -> "my name is "+s;
        String result = function1.andThen(function2).apply("zhangSan");
        System.out.println(result);
    }
    

    执行结果:

    如结果所示:

    andThen先执行function1 后执行function2。

    • identity

    测试代码:

    public static void main(String[] args) {
        Stream<String> stream = Stream.of("order", "good", "lab", "warehouse");
        Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length));
        System.out.println(map);
    }
    

    执行结果:

    3.2 Supplier

    Supplier的表现形式为不接受参数、只返回数据。

    3.2.1 源码
    @FunctionalInterface
    public interface Supplier<T> {
        /**
         * Gets a result.
         *
         * @return a result
         */
        T get();
    }
    
    3.2.2 方法说明

    get:抽象方法。通过实现返回T。

    3.2.3 方法举例
    public class SupplierTest {
        SupplierTest(){
            System.out.println(Math.random());
            System.out.println(this.toString());
        }
    }
        public static void main(String[] args) {
            Supplier sup = SupplierTest::new;
            System.out.println("调用一次");
            sup.get();
            System.out.println("调用二次");
            sup.get();
    }
    

    执行结果:

    如结果所示:Supplier建立时并没有创建新类,每次调用get返回的值不是同一个。

    3.3 Consumer

    Consumer接收一个参数,没有返回值。

    3.3.1 源码
    @FunctionalInterface
    public interface Consumer {
        void accept(T t);
        default Consumer andThen(Consumer after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); };
        }
    }
    
    3.3.2 方法说明
    • accept:对给定参数T执行一些操作。
    • andThen:按顺序执行Consumer -> after ,如果执行操作引发异常,该异常被传递给调用者。
    3.3.3 方法举例
    public static void main(String[] args) {
        Consumer<String> consumer = s -> System.out.println("consumer_"+s);
        Consumer<String> after = s -> System.out.println("after_"+s);
        consumer.accept("isReady");
        System.out.println("========================");
        consumer.andThen(after).accept("is coming");
    }
    

    执行结果:

    如结果所示:对同一个参数T,通过andThen 方法,先执行consumer,再执行fater。

    3.4 Runnable

    Runnable:Runnable的表现形式为即没有参数也没有返回值。

    3.4.1 源码
    @FunctionalInterface
    public interface Runnable {
        public abstract void run();
    }
    
    3.4.2 方法说明

    run:抽象方法。run方法实现具体的内容,需要将Runnale放入到Thread中,通过Thread类中的start()方法启动线程,执行run中的内容。

    3.4.3 方法举例
    public class TestRun implements Runnable {
        @Override
        public void run() {
            System.out.println("TestRun is running!");
        }
    }
        public static void main(String[] args) {
            Thread thread = new Thread(new TestRun());
            thread.start();
        }
    

    执行结果:

    如结果所示:当线程实行start方法时,执行Runnable 的run方法中的内容。

    4 Function接口用法

    Function的主要用途是可以通过lambda 表达式实现方法的内容。

    4.1 差异处理

    原代码:

    @Data
    public class User {
        /**
         * 姓名
         */
        private String name;
        /**
         * 年龄
         */
        private int age;
        /**
         * 组员
         */
        private List parters;
    }
        public static void main(String[] args) {
            User user =new User();
            if(user ==null ||user.getAge() <18 ){
                throw new RuntimeException("未成年!");
            }
    }
    

    执行结果:

    使用Function接口后的代码:

    @FunctionalInterface
    public interface testFunctionInfe {
        /**
         * 输入异常信息
         * @param message
         */
        void showExceptionMessage(String message);
    }
        public static testFunctionInfe doException(boolean flag){
            return (message -> {
                if (flag){
                    throw new RuntimeException(message);
                }
            });
        }
        public static void main(String[] args) {
            User user =new User();
            doException(user ==null ||user.getAge() <18).showExceptionMessage("未成年!");
    }
    

    执行结果:

    使用function接口前后都抛出了指定的异常信息。

    4.2 处理if…else…

    原代码:

    public static void main(String[] args) {
        User user =new User();
        if(user==null){
            System.out.println("新增用户");
        }else {
            System.out.println("更新用户");
        }
    }
    

    使用Function接口后的代码:

    public static void main(String[] args) {
        User user =new User();
        Consumer trueConsumer = o -> {
            System.out.println("新增用户");
        };
        Consumer falseConsumer= o -> {
            System.out.println("更新用户");
        };
        trueOrFalseMethdo(user).showExceptionMessage(trueConsumer,falseConsumer);
    }
    public static testFunctionInfe trueOrFalseMethdo(User user){
        return ((trueConsumer, falseConsumer) -> {
            if(user==null){
                trueConsumer.accept(user);
            }else {
                falseConsumer.accept(user);
            }
        });
    }
    @FunctionalInterface
    public interface testFunctionInfe {
        /**
         * 不同分处理不同的事情
         * @param trueConsumer
         * @param falseConsumer
         */
        void showExceptionMessage(Consumer trueConsumer,Consumer falseConsumer);
    }
    

    执行结果:

    4.3 处理多个if

    原代码:

    public static void main(String[] args) {
        String flag="";
        if("A".equals(flag)){
            System.out.println("我是A");
        }else if ("B".equals(flag)) {
            System.out.println("我是B");
        }else if ("C".equals(flag)) {
            System.out.println("我是C");
        }else {
            System.out.println("没有对应的指令");
        }
    }
    

    使用Function接口后的代码:

    public static void main(String[] args) {
        String flag="B";
        Map<String, Runnable> map =initFunctionMap();
        trueOrFalseMethdo(map.get(flag)==null).showExceptionMessage(()->{
            System.out.println("没有相应指令");
        },map.get(flag));
    }
    public static   Map<String, Runnable> initFunctionMap(){
        Map<String,Runnable> result  = Maps.newHashMap();
        result.put("A",()->{System.out.println("我是A");});
        result.put("B",()->{System.out.println("我是B");});
        result.put("C",()->{System.out.println("我是C");});
        return result;
    }
    public static testFunctionInfe trueOrFalseMethdo(boolean flag){
        return ((runnable, falseConsumer) -> {
            if(flag){
                runnable.run();
            }else {
                falseConsumer.run();
            }
        });
    }
    

    执行结果:

    5 总结

    Function函数式接口是java 8新加入的特性,可以和lambda表达式完美结合,是非常重要的特性,可以极大的简化代码。

  • 相关阅读:
    Linux线程同步(下)
    【Java集合】HashMap系列(五)——HashMap在JDK1.7和JDK1.8比较总结及常见面试题
    ASP.NET Core 开发 Web API
    基于视频技术与AI检测算法的体育场馆远程视频智能化监控方案
    内网怎么使用通义灵码插件?
    vue-cli的安装
    [轻笔记] label smoothing(标签平滑)
    三种典型电气减压比例阀线性度和短期重复性的对比考核试验
    数字货币中短线策略(数据+回测+实盘)
    腾讯云CVM服务器数据盘挂载
  • 原文地址:https://www.cnblogs.com/Jcloud/p/16934938.html