Java函数式编程

世上唯一不能复制的是时间,唯一不能重演的是人生,唯一不劳而获的是年龄。该怎么走,过什么样的生活,全凭自己的选择和努力。人生很贵,请别浪费!与智者为伍,与良善者同行。Java函数式编程,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

总结

  • 现在主流的编程范式主要有三种,面向过程、面向对象和函数式编程。函数式编程作为一种补充,有很大存在、发展和学习的意义。
  • 函数内部涉及的变量都是局部变量,不会像面向对象编程那样,共享类成员变量,也不会像面向过程编程那样,共享全局变量。
  • 函数式接口可以将函数作为一个参数传入方法中进行使用。

概述

函数式编程因其编程的特殊性,仅在科学计算、数据处理、统计分析等领域,才能更好地发挥它的优势,所以它并不能完全替代更加通用的面向对象编程范式。但是作为一种补充,它也有很大存在、发展和学习的意义。

函数式编程更符合数学上函数映射的思想。具体到编程语言层面,我们可以使用Lambda表达式来快速编写函数映射,函数之间通过链式调用连接到一起,完成所需业务逻辑。Java的Lambda表达式是后来才引入的,由于函数式编程在并行处理方面的优势,正在被大量应用在大数据计算领域。

编程范式

现在主流的编程范式主要有三种,面向过程、面向对象和函数式编程。

面向对象编程最大的特点是:以类、对象作为组织代码的单元以及它的四大特性。

面向过程编程最大的特点是:以函数作为组织代码的单元,数据与方法相分离。

  • 函数式编程并非一个很新的东西,早在50多年前就已经出现了。近几年,函数式编程越来越被人关注,出现了很多新的函数式编程语言,比如Clojure、Scala、Erlang等。一些非函数式编程语言也加入了很多特性、语法、类库来支持函数式编程,比如Java、Python、Ruby、JavaScript等。除此之外,Google Guava也有对函数式编程的增强功能。
  • 函数式编程因其编程的特殊性,仅在科学计算、数据处理、统计分析等领域,才能更好地发挥它的优势,所以它并不能完全替代更加通用的面向对象编程范式。但是作为一种补充,它也有很大存在、发展和学习的意义。
  • 函数式编程中的“函数”,并不是指我们编程语言中的“函数”概念,而是指数学“函数”或者“表达式”(例如:y=f(x))。不过,在编程实现的时候,对于数学“函数”或“表达式”,我们一般习惯性地将它们设计成函数。
  • 函数式编程最独特的地方在于它的编程思想。函数式编程认为程序可以用一系列数学函数或表达式的组合来表示。函数式编程是程序面向数学的更底层的抽象,将计算过程描述为表达式。
  • 并不是所有的程序都适合这么做。函数式编程有它自己适合的应用场景,比如科学计算、数据处理、统计分析等。在这些领域,程序往往比较容易用数学表达式来表示,比起非函数式编程,实现同样的功能,函数式编程可以用很少的代码就能搞定。但是,对于强业务相关的大型业务系统开发来说,费劲吧啦地将它抽象成数学表达式,硬要用函数式编程来实现,显然是自讨苦吃。相反,在这种应用场景下,面向对象编程更加合适,写出来的代码更加可读、可维护。
  • 函数式编程跟面向过程编程一样,也是以函数作为组织代码的单元。不过,它跟面向过程编程的区别在于,它的函数是无状态的。何为无状态?简单点讲就是,**函数内部涉及的变量都是局部变量,不会像面向对象编程那样,共享类成员变量,也不会像面向过程编程那样,共享全局变量。**函数的执行结果只与入参有关,跟其他任何外部变量无关。同样的入参,不管怎么执行,得到的结果都是一样的。这实际上就是数学函数或数学表达式的基本要求。

Java对函数式编程的支持

Java为函数式编程引入了三个新的语法概念:Stream类、Lambda表达式和函数接口(Functional Inteface)。Stream类用来支持通过“.”级联多个函数操作的代码编写方式;引入Lambda表达式的作用是简化代码编写;函数接口的作用是让我们可以把函数包裹成函数接口,来实现把函数当做参数一样来使用(Java 不像C那样支持函数指针,可以把函数直接当参数来使用)。

  • stream: stream “.”表示调用某个对象的方法。为了支持上面这种级联调用方式,我们让每个函数都返回一个通用的Stream类对象。在Stream类上的操作有两种:中间操作和终止操作。中间操作返回的仍然是Stream类对象,而终止操作返回的是确定的值结果。
  • map、filter是中间操作,返回Stream类对象,可以继续级联其他操作;max是终止操作,返回的是OPTIONAL类对象。
  • lambda: lambda表达式在Java中只是一个语法糖而已,底层是基于函数接口来实现的。Lambda表达式包括三部分:输入、函数体、输出。
  • 函数接口: Java没有函数指针这样的语法。所以它通过函数接口,将函数包裹在接口中,当作变量来使用。实际上,函数接口就是接口。不过,它也有自己特别的地方,那就是要求只包含一个未实现的方法。因为只有这样,Lambda表达式才能明确知道匹配的是哪个方法。如果有两个未实现的方法,并且接口入参、返回值都一样,那Java在翻译Lambda表达式的时候,就不知道表达式对应哪个方法了。
public static void stream(String[] args) {
        Optional<Integer> result = Stream.of("f", "ba", "hello") // of返回Stream<String>对象
                .map(s -> s.length()) // map返回Stream<Integer>对象
                .filter(l -> l <= 3) // filter返回Stream<Integer>对象
                .max((o1, o2) -> o1 - o2); // max终止操作:返回Optional<Integer>
        System.out.println(result.get()); // 输出2
    }

函数式接口

@FunctionalInterface注解使用场景: 一个接口只要满足只有一个抽象方法的条件,即可以当成函数式接口使用,有没有 @FunctionalInterface 都无所谓。

如果使用了此注解,再往接口中新增抽象方法,编译器就会报错,编译不通过。换句话说,@FunctionalInterface 就是一个承诺,承诺该接口世世代代都只会存在这一个抽象方法。因此,凡是使用了这个注解的接口,开发者可放心大胆的使用Lambda来实例化。当然误用 @FunctionalInterface 带来的后果也是极其惨重的:如果哪天你把这个注解去掉,再加一个抽象方法,则所有使用Lambda实例化该接口的客户端代码将全部编译错误。

自定义函数式编程接口过程:

  • 通过 @FunctionalInterface 注解,申明一个函数式接口。
  • 在方法中使用函数接口作为入参使用;
  • 调用方法,传入函数接口的实现方法。
public class MyFuncInterface {
    /**
     * 声明一个函数式接口
     * @param <T>
     */
    @FunctionalInterface
    public interface ToLongFunction<T> {
        long applyAsLong(T value);
    }

    /**
     * 工具函数定义使用函数接口作为参数
     */
    public static class Util{
         public static Long mapToLong(ToLongFunction<? super Collection> mapper, List<String> val) {
            Objects.requireNonNull(mapper);
            return mapper.applyAsLong(val);
        }
    }

    /**
     * 使用例子
     * @param args
     */
    public static void main(String[] args) {
        List<String> arr = Arrays.asList("ddd", "222", "3333");
        Long size = Util.mapToLong((item)-> item.size(),arr);
        System.out.println(size);
    }
}

参考

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/221986.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!