在维基百科中给出了详细的定义,函数式编程(英语:functional programming)或称函数程序设计,又称泛函编程,是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。
在函数式编程中,由于数据全部都是不可变的,所以没有并发编程的问题,是多线程安全的。可以有效降低程序运行中所产生的副作用,对于快速迭代的项目来说,函数式编程可以实现函数与函数之间的热切换而不用担心数据的问题,因为它是以函数作为最小单位的,只要函数与函数之间的关系正确即可保证结果的正确性。
函数式编程的表达方式更加符合人类日常生活中的语法,代码可读性更强。实现同样的功能函数式编程所需要的代码比面向对象编程要少很多,代码更加简洁明晰。函数式编程广泛运用于科学研究中,因为在科研中对于代码的工程化要求比较低,写起来更加简单,所以使用函数式编程开发的速度比用面向对象要高很多,如果是对开发速度要求较高但是对运行资源要求较低同时对速度要求较低的场景下使用函数式会更加高效。
由于所有的数据都是不可变的,所以所有的变量在程序运行期间都是一直存在的,非常占用运行资源。同时由于函数式的先天性设计导致性能一直不够。虽然现代的函数式编程语言使用了很多技巧比如惰性计算等来优化运行速度,但是始终无法与面向对象的程序相比,当然面向对象程序的速度也不够快。
函数式编程虽然已经诞生了很多年,但是至今为止在工程上想要大规模使用函数式编程仍然有很多待解决的问题,尤其是对于规模比较大的工程而言。如果对函数式编程的理解不够深刻就会导致跟面相对象一样晦涩难懂的局面。
Consumer接口的源码只有两个方法,一个方法用来接收入口参数,另外一个是为了实现流式操作:
public static void main(String[] args) { //1:平平无奇:直接创建接口并重载 Consumer<String> consumer1 = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; Stream<String> stream1 = Stream.of("spring", "summer", "autumn", "winter"); stream1.forEach(consumer1); System.out.println("********************"); //2:偶有起色: 使用Java8另外一个特性,方法引用 Consumer consumer2 = System.out::println; Stream<String> stream2 = Stream.of("spring", "summer", "autumn", "winter"); stream2.forEach(consumer2); System.out.println("********************"); //3:精彩绝伦,使用lambda表达式 Consumer<String> consumer3 = (s) -> System.out.println(s);//lambda表达式返回的就是一个Consumer接口 Stream<String> stream3 = Stream.of("spring", "summer", "autumn", "winter"); stream3.forEach(consumer3); } //改写自:https://cloud.tencent.com/developer/article/1488128
upplier 接口是一个供给型的接口,其实,说白了就是一个容器,可以用来存储数据,然后可以供其他方法使用的这么一个接口,是不是很明白了,如果还是不明白,看看下面的例子,一定彻底搞懂!
/** * Supplier接口测试2,使用需要Supplier的接口方法 */ @Test public void test_Supplier2() { Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5); //返回一个optional对象 Optional<Integer> first = stream.filter(i -> i > 4) .findFirst(); //optional对象有需要Supplier接口的方法 //orElse,如果first中存在数,就返回这个数,如果不存在,就放回传入的数 System.out.println(first.orElse(1)); System.out.println(first.orElse(7)); System.out.println("********************"); Supplier<Integer> supplier = new Supplier<Integer>() { @Override public Integer get() { //返回一个随机值 return new Random().nextInt(); } }; //orElseGet,如果first中存在数,就返回这个数,如果不存在,就返回supplier返回的值 System.out.println(first.orElseGet(supplier)); }
除了上面使用的 Supplier 接口,还可以使用下面这些 Supplier 接口。 IntSupplier 、DoubleSupplier 、LongSupplier 、BooleanSupplier
,使用方法和上面一样。
Predicate 接口是一个谓词型接口,其实,这个就是一个类似于 bool 类型的判断的接口,后面看看就明白了。
/** * Predicate谓词测试,谓词其实就是一个判断的作用类似bool的作用 */ @Test public void test_Predicate() { //① 使用Predicate接口实现方法,只有一个test方法,传入一个参数,返回一个bool值 Predicate<Integer> predicate = new Predicate<Integer>() { @Override public boolean test(Integer integer) { if(integer > 5){ return true; } return false; } }; System.out.println(predicate.test(6)); System.out.println("********************"); //② 使用lambda表达式, predicate = (t) -> t > 5; System.out.println(predicate.test(1)); System.out.println("********************"); }
/** * Predicate谓词测试,Predicate作为接口使用 */ @Test public void test_Predicate2() { //① 将Predicate作为filter接口,Predicate起到一个判断的作用 Predicate<Integer> predicate = new Predicate<Integer>() { @Override public boolean test(Integer integer) { if(integer > 5){ return true; } return false; } }; Stream<Integer> stream = Stream.of(1, 23, 3, 4, 5, 56, 6, 6); List<Integer> list = stream.filter(predicate).collect(Collectors.toList()); list.forEach(System.out::println); System.out.println("********************"); }
public static void main(String[] args) { System.out.println(commonMethod(doError1)); System.out.println(commonMethod(doError2)); } public static String commonMethod(Supplier<String> doError){ try { System.out.println("do Somthing"); throw new Exception(); } catch (Exception e) { log.error("error happen"); return doError.get(); } } private static Supplier<String> doError1 = () -> { System.out.println(" do error1"); return "erro1"; }; private static Supplier<String> doError2 = () -> { System.out.println(" do error2"); return "erro2"; };
Function 接口是一个功能型接口,它的一个作用就是转换作用,将输入数据转换成另一种形式的输出数据。
public static void main(String[] args) { applyTest(); andThenTest(); composeTest(); test(); } //1、apply 示例 private static void applyTest() { //示例1:利用lambda方式实现一个funciton,将String转换为Integer Function<String, Integer> function = x -> Integer.parseInt(x); Integer a = function.apply("100"); System.out.println(a.getClass()); } //2、andThen 示例——实现一个函数 y=10x + 10; private static void andThenTest() { //先执行 10 * x Function<Integer, Integer> function2 = x -> 10 * x; //通过andThen在执行 这里的x就等于上面的10 * x的值 function2 = function2.andThen(x -> x + 10); System.out.println(function2.apply(2)); } //3、compose 示例-实现一个函数 y=(10+x)2 private static void composeTest() { Function<Integer, Integer> function3 = x -> x * 2; //先执行 x+10 在执行(x+10)*2顺序与上面相反 function3 = function3.compose(x -> x + 10); System.out.println(function3.apply(3)); } //4、综合示例 //使用compose()、andThen()实现一个函数 y=(10+x)*2+10; private static void test() { //真正执行的第二步 Function<Integer, Integer> function4 = x -> x * 2; //真正执行的第一步 function4 = function4.compose(x -> x + 10); //真正执行的第三步 function4 = function4.andThen(x -> x + 10); System.out.println(function4.apply(3)); }
/** * 函数型接口:Function<R, T> */ @Test public void test3 () { String s = strOperar(" asdf ", x -> x.substring(0, 2)); System.out.println(s); String s1 = strOperar(" asdf ", x -> x.trim()); System.out.println(s1); } /** * 字符串操作 * @param str 需要处理得字符串 * @param fun Function接口 * @return 处理之后得字符传 */ public String strOperar(String str, Function<String, String> fun) { return fun.apply(str); }
除了上面使用的 Function 接口,还可以使用下面这些 Function 接口。 IntFunction 、DoubleFunction 、LongFunction 、ToIntFunction 、ToDoubleFunction 、DoubleToIntFunction 等等,使用方法和上面一样。