java函数式编程

简介: 前言2014年,Oracle发布了Java8新版本。这对java来说是一个里程碑式的版本。他最主要的改进就是增加了函数式编程的功能(为了解决java程序总是冗长的问题),或许会感到奇怪,函数式编程和并发似乎没什么关系,但是java中与并发相关的API的实现,却是以函数式编程的范式来实现的。

前言

2014年,Oracle发布了Java8新版本。这对java来说是一个里程碑式的版本。他最主要的改进就是增加了函数式编程的功能(为了解决java程序总是冗长的问题),或许会感到奇怪,函数式编程和并发似乎没什么关系,但是java中与并发相关的API的实现,却是以函数式编程的范式来实现的。所以为了更好的理解这些功能,需要先学习下函数式编程。

java8的函数式编程一些特点

函数作为一等公民

我理解的,一句话来总结就是函数可以作为另一个函数的参数或者返回值吧!。如下面一段Javascript代码:

function f1(){
    var n=1;
    function f2(){
        alert(n);
    }
    return f2;
}
var result= f1();
result();//1

上述代码f1返回了f2并赋值给了result,此时result就是一个函数,指向f2,调用result就会打印n的值

无副作用

函数的副作用指的是函数除了返回值外,还修改了函数外部的状态,不如修改了一个全局变量。可以想象,这样当系统出现故障时,我们很难判断问题是由哪一个函数引起的,对调试和追踪是没有好处的。如果函数都是显式函数,那么函数的执行显然不会收到外部或者全局信息的干扰,有利于调试和追踪。所谓显式函数是指函数与外界交换数据的唯一渠道就是参数和返回值,显式函数不会读取或者修改函数的外部状态。而与之对应的隐式函数还会读取外部信息或者修改外部信息。
然而 实际上完全的无副作用是不可能是实现的,系统总是需要获取或者修改外部信息的,同时,模块之间的交互也极有可能是通过共享变量进行的。因此大多数的函数式编程都允许副作用的存在,如Clojure等,但是与面向对象相比,这种函数调用的副作用,在函数式编程里,需要进行一些有效的限制。

申明式的(Declarative)

函数式编程是申明式的的编程方式。相对与命令式而言,命令式的·程序者总是喜欢使用大量的 可变对象和指令。我们总是喜欢创建对象和变量,并且修改他们的状态或值,或者喜欢提供一系列指令要求程序执行。而在函数式编程中,那些细节的指令将会更好的被函数库所封装,我们只需要提出我们的要求,申明我们的用意即可。
下面,我们来看下两种方式的打印数组的代码:

//命令式
public     static void imperative(){
    int [] array={1,2,3,4};
    for(int i=0;i<array.length;i++){
    System.out.println("array[i]");
    }    
}

申明式

public  static void declarative(){
     int [] array={1,2,3,4};
     Arrays.stream(array).foreach(System.out::println);
    }

不变的对象

在函数式编程中几乎所有的对象都不会被轻易的i修改。下面我么看个例子来裂解:

static int [] ar={1,2,3};
Arrays.stream(ar).map((x)->x=x+1).foreach(System.out::println);
System.out.println();
Arrays.stream(ar).foreach(System.out::println);

代码第二行看起来对数组每个元素进行了加一操作,但通过最后一行打印元素时会发现,数组成员并没有发生变化。
在使用函数式编程的时候,这种状态几乎是一种常态,几乎所有的对象都拒绝被修改。这非常类似与不变模式。

易于并行

由于对象都处于不变状态,因此函数式编程更加易于并行。不需要同步,也没有锁机制,其性能也会比较好。

更少的代码

不难理解,函数式编程的范式更加紧凑而且简洁。

函数式编程的基础

FunctionalInterface注解

java8提出了函数式接口的定义。简单来说,函数式接口,就是之定义了单一抽象方法的接口。如下面的定义:

@FunctionalInterface
public static interface IntHandler{
    void handle(int i);
}

注释@FunctionalInterface表示这是一个函数式接口,该注释与@Override注释类似,不管该方法是否标注了该注释,只要满足重载或者函数式接口的定义,编译器就会把它看做重载方法或者函数式接口。
需要注意的是,并不是函数式接口只能有一个方法,首先接口运行存在实例方法,其次,任何被java.lang.Object实现的方法,都不能视为抽象方法。
比如下面 也是一个标准的函数式接口:

@FunctionalInterface
public static interface IntHandler{
    void handle(int i);
    boolean equals(Object obj);
}

接口默认方法

在java8之前,接口只能包含抽象方法。java8之后,接口还可以包含若干个实例方法,是的java8有了类似多继承的能力。一个对象实例,将拥有来自不同接口的实例方法。

lambda表达式

lambda表达式可以说是函数式编程的核心。lambda表达式就是匿名函数,他是一段没有函数名的函数体,可以直接作为参数传递给调用者。
下面看一段lambda表达式的使用:

List<Integer> numbers=Arrays.aslist    (1,2,3,4);
numbers.forEach((Integer value) -> System.out.println(value));

上述代码遍历的输出列表的元素。
和匿名对象一样,lambda表达式也可以访问外部的局部变量,如下:

final int num=2;
Function<Integer,Integer> stringConverter = (from) -> from*num;
//num++;
System.out.println(stringConverter.apply(3));

上述代码可以编译通过,输出结果6,与匿名内部对象一样,在这种情况外部的num变量必须申明为final,才能保证lambda表达式合法的访问它。

但是对lambda表达式而言,即使去掉final,程序依然会编译通过。但是我们就不能修改num的值了,java8会自动将lambda表达式中用到的变量视为final。

方法引用

方法引用是java8中提出的用来简化lambda表达式的一种手段。它通过类名和方法名来定位一个静态方法和实例方法。
方法引用在java8中使用的非常灵活。总的来说,分为以下几种:

  • 静态方法引用:ClassName::methodName
  • 实例上的实例方法引用:instanceReference::methodName
  • 超类上的实例方法引用: super::methodName
  • 类型上的实例方法引用: ClassName::methodName
  • 构造方法引用: Class::new
  • 数组构造方法引用: TypeName[]::new

总结来说 一般::前面表示类名或者实例名,后半部分表示方法名,构造函数用new表示。
应该容易理解,此处就不在用例子说明了。

java函数式编程的简单实践

下面我们以一个java8流的例子。来简单显示java函数式编程:

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.function.IntConsumer;

public class Funtionpr {
    static int [] arr= {1,2,3,4};
    /**
     * @param args
     */
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
//        for (int i:arr) {
//            System.out.println(i);
//        }
        
//        Arrays.stream(arr).forEach(new IntConsumer() {
//            @Override
//            public void accept(int value) {
//                System.out.println(value);
//            }
//        });
        
        Arrays.stream(arr).forEach(( x)->System.out.println(x)
        );
    }

}
相关文章
|
22天前
|
Java
Java8函数式编程
Java8函数式编程
24 2
|
3月前
|
Java 程序员 API
解锁Java新纪元:Lambda表达式——让你的代码秒变高大上,函数式编程不再是梦!
【8月更文挑战第25天】Java 8 推出了革命性的 Lambda 表达式特性,这是一种匿名函数形式,支持任意数量参数及返回值,简化了代码编写。其基本语法为 `(parameters) -&gt; expression` 或 `(parameters) -&gt; { statements; }`。例如,遍历集合可从使用匿名内部类变为简洁的 `names.forEach(name -&gt; System.out.println(name))`。
53 0
|
6天前
|
Java API
Java中的Lambda表达式与函数式编程####
【10月更文挑战第29天】 本文将深入探讨Java中Lambda表达式的实现及其在函数式编程中的应用。通过对比传统方法,我们将揭示Lambda如何简化代码、提高可读性和维护性。文章还将展示一些实际案例,帮助读者更好地理解和应用Lambda表达式。 ####
|
7天前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
17 4
|
11天前
|
Java API 开发者
Java中的Lambda表达式与函数式编程####
在Java的演变过程中,Lambda表达式和函数式编程的引入无疑是一次重大的飞跃。本文将深入探讨Lambda表达式的定义、用法及优势,并结合实例说明如何在Java中利用Lambda表达式进行函数式编程。通过对比传统编程方式,揭示Lambda表达式如何简化代码、提高开发效率和可维护性。 ####
|
21天前
|
Java 数据处理
|
1月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
24 1
|
2月前
|
并行计算 Java 测试技术
探索Java中的函数式编程
在本文中,我们将深入探讨Java中的函数式编程。我们会先了解什么是函数式编程以及为什么它如此重要。然后,通过一些简单的代码示例,展示如何在Java中应用函数式编程概念。最后,讨论在实际项目中如何利用函数式编程来提高代码的可读性和效率。
29 7
|
2月前
|
Java API 开发者
探索Java中的函数式编程
本文深入探讨了Java中的函数式编程,这是一种强调使用不可变数据和避免共享状态的编程范式。我们将从基础概念、核心特性以及实际应用案例三个方面,全面解析函数式编程在Java中的魅力和价值。
|
2月前
|
Java C语言
5-13|Java的函数式编程
5-13|Java的函数式编程