浅谈Java 8的函数式编程
当前位置:以往代写 > JAVA 教程 >浅谈Java 8的函数式编程
2019-06-14

浅谈Java 8的函数式编程

浅谈Java 8的函数式编程

副标题#e#

关于“Java 8为Java带来了函数式编程”已经有了许多接头,但这句话的真正意义是什么?

本文将接头函数式,它对一种语言或编程方法意味着什么。在答复“Java 8的函数式编程怎么样”之前,我们先看看Java的演变,出格是它的范例系统,我们将看到Java 8的新特性,出格是Lambda表达式如何改变Java的风光,并提供函数式编程气势气魄的主要优势。

函数式编程语言是什么?

函数式编程语言的焦点是它以处理惩罚数据的方法处理惩罚代码。这意味着函数应该是第一品级(First-class)的值,而且可以或许被赋值给变量,通报给函数等等。

事实上,许多函数式语言比这走得更远,将计较和算法看得比它们操纵的数据更重要。个中有些语言想疏散措施状态和函数(以一种看起来有点对立的方法,利用面向工具的语言,这凡是会将它们接洽得更细密)。

Clojure编程语言就是一个这样的例子,尽量它运行于基于类的Java虚拟机,Clojure的本质是函数式语言,而且在高级语言源措施中不直接发布类和工具(尽量提供了与Java精采的互操纵性)。

下面显示的是一个Clojure函数,用于处理惩罚日志,是一等国民(First-class citizen),而且不需要绑定一个类而存在。

(defn build-map-http-entries [log-file]
 (group-by :uri (scan-log-for-http-entries log-file)))

当写在函数中的措施,对给定的输入(岂论措施中的其它状态如何)老是返回沟通的输出,而且不会发生其它影响,可能改变任何措施状态,这时候函数式编程是最有用的。它们的行为与数学函数沟通,有时候把遵循这个尺度的函数称为“纯”函数。

纯函数的庞大长处是它们更容易推论,因为它们的操纵不依赖于外部状态。函数可以或许很容易地团结在一起,这在开拓者事情流气势气魄中很常见,譬喻Lisp方言和其它具有强函数传统的语言中很普遍的REPL(Read, Execute, Print, Loop)气势气魄。

非函数式编程语言中的函数式编程

一种语言是不是函数式并不长短此即彼的状态,实际上,语言存在于图谱上。在最结尾,根基上是强制函数式编程,凡是克制可变的数据布局。Clojure就是一种不接管可变数据的语言。

不外,也有一些其它语言,凡是以函数方法编程,但语言并不强制这一点。Scala就是一个例子,它混和了面向工具和函数式语言。答允函数作为值,譬喻:

val sqFn = (x: Int) => x * x

同时保存与Java很是靠近的类和工具语法。

另一个极度,虽然,利用完全非函数式语言举办函数式编程是大概的,譬喻C语言,只要维持好符合的措施员准则和老例。

思量到这一点,函数式编程应该被看作是有两个因素的函数,个中一个与编程语言相关,另一个是用该语言编写的措施:

1)底层编程语言在多洪流平上支持,可能强制函数式编程?

2)这个特定的措施如何利用语言提供的函数式特性?它是否制止了非函数式特性,譬喻可变状态?


#p#副标题#e#

Java的一些汗青

Java是一种顽强己见的语言,它具有很好的可读性,低级措施员很容易上手,具有恒久不变性和可支持性。但这些设计抉择也支付了必然的价钱:冗长的代码,范例系统与其它语言对比显得缺乏弹性。

然而,Java的范例系统已经在演化,固然在语言的汗青傍边相比拟力慢。我们来看看这些年来它的一些形式。

Java最初的范例系统

Java最初的范例系统至今已经高出15年了。它简朴而清晰,范例包罗引用范例和根基范例。类、接口可能数组属于引用范例。

类是Java平台的焦点,类是Java平台将会加载、或链接的成果的根基单元,所有要执行的代码都必需驻留于一个类中。

接口不能直接实例化,而是要通过一个实现了接口API的类。

数组可以包括根基范例、类的实例可能其它数组。

根基范例全部由平台界说,措施员不能界说新的根基范例。

从最早开始,Java的范例系统一直僵持很重要的一点,每一种范例都必需有一个可以被引用的名字。这被称为“标明范例(Nominative typing)”,Java是一种强标明范例语言。

纵然是所谓的“匿名内部类”也仍然有范例,措施员必需能引用它们,才气实现那些接口范例:

Runnable r = new Runnable() { public void run() { System.out.println("Hello World!"); } };

换种说法,Java中的每个值要么是根基范例,要么是某个类的实例。

定名范例(Named Type)的其它选择

其它语言没有这么沉沦定名范例。譬喻,Java没有这样的Scala观念,一个实现(特定签名的)特定要领的范例。在Scala中,可以这样写:

x : {def bar : String}

#p#分页标题#e#

记着,Scala在右侧标示变量范例(冒号后头),所以这读起来像是“x是一种范例,它有一个要领bar返回String”。我们能用它来界说雷同这样的Scala要领:

def showRefine(x : {def bar : String}) = { print(x.bar) }

然后,假如我们界说一个符合的Scala工具:

object barBell { def bar = "Bell" }

然后挪用showRefine(barBell),这就是我们等候的事:

showRefine(barBell) Bell

这是一个精化范例(Refinement typing)的例子。从动态语言转过来的措施员大概熟悉“鸭子范例(Duck typing)”。布局精化范例(Structural refinement typing)是雷同的,除了鸭子范例(假如它走起来像鸭子,叫起来像鸭子,就可以把它看成鸭子)是运行时范例,而这些布局精化范例浸染于编译时。

在完全支持布局精化范例的语言中,这些精化范例可以用在措施员大概期望的任那里所,譬喻要领参数的范例。而Java,相反地,不支持这样的范例(除了几个稍微独特的边沿例子)。

#p#副标题#e#

Java 5范例系统

Java 5的宣布为范例系统带来了三个主要新特性,列举、注解和泛型。

列举范例(Enum)在某些方面与类相似,可是它的属性只能是指定命量的实例,每个实例都差异而且在类描写中指定。主要用于“范例安详的常量”,而不是其时普遍利用的小整数常量,列举结构同时还答允附加的模式,有时候这很是有用。

注解(Annotation)与接口相关,声明注解的要害字是@interface,以@开始暗示这是个注解范例。正如名字所发起的,它们用于给Java代码元素做注释,提供附加信息,但不影响其行为。此前,Java曾利用“标志接口(Marker interface)”来提供这种元数据的有限形式,但注解被认为更有机动性。

Java泛型提供了参数化范例,其想法是一种范例能饰演其它范例工具的“容器”,无需体贴被包括范例的详细细节。装配到容器中的范例凡是称为范例参数。

Java 5引入的特性中,列举和注解为引用范例提供了新的形式,这需要编译器非凡处理惩罚,而且有效地从现有范例层级布局疏散。

泛型为Java的范例系统增加了显著特另外巨大性,不只仅因为它们是纯粹的编译时特性,还要求Java开拓人员应留意,编译时和运行时的范例系统互相略有差异。

尽量有这些变革,Java仍然保持标明范例。范例名称此刻包罗List(读作:“List-of-String”)和Map, CachedObject>(“Map-of-Class-of-Unknown-Type-to-CachedObject”),但这些仍然是定名的范例,而且每个非根基范例的值仍是某个类的实例。

Java 6和7引入的特性

Java 6根基上是一本机能优化和类库加强的版本。范例系统的独一变革是扩大注解脚色,宣布可插拔注解处理惩罚成果。这对大大都开拓者没有任何影响,Java 6中也没有真正提供可插拔范例系统。

Java 7的范例系统没有重大改变。仅有的一些新特性,看起来都很相似:

javac编译器中范例揣度的小改造。

签名多态性分配(Signature polymorphic dispatch),用于要领句柄(Method handle)的实现细节,而这在Java 8中又反过来用于实现Lambda表达式。

Multi-catch提供了一些“代数数据范例”的小跟踪信息,但这些完全是javac内部的,对最终用户措施员没有任何影响。

Java 8的范例系统

纵观其汗青,Java根基上已经过其范例系统所界说。它是语言的焦点,而且严格遵守着标明范例。从实际环境来看,Java范例系统在Java 5和7之间没有太大变革。

乍一看,我们大概期望Java 8改变这种状况。究竟,一个简朴的Lambda表达式好像让我们移除了标明范例:

() -> { System.out.println("Hello World!"); }

这是个没有名字、没有参数的要领,返回void。它仍然是完全静态范例的,但此刻是匿名的。

我们逃脱了名词的王国?这真的是Java的一种新的范例形式?

也许不幸的是,谜底是否认的。JVM上运行的Java和其它语言,很是严格地限制在类的观念中。类加载是Java平台的安详和验证模式的中心。简朴地说,不通过类来暗示一种范例,这长短常很是难的。

Java 8没有建设新的范例,而是通过编译器将Lambda表达式自动转换成一个类的实例。这个类由范例揣度来抉择。譬喻:

Runnable r = () -> { System.out.println("Hello World!"); };

#p#分页标题#e#

右侧的Lambda表达式是个有效的Java 8的值,但其范例是按照左侧值揣度的,因此它实际上是Runnable范例的值。需要留意的是,假如没有正确地利用Lambda表达式,大概会导致编译器错误。纵然是引入了Lambda,Java也没有改变这一点,仍然遵守着标明范例。

Java 8的函数式编程怎么样?

最后,让我们回到本文开头提出的问题,“Java 8的函数式编程怎么样?”

Java 8之前,假如开拓者想以函数式气势气魄编程,他或她只能利用嵌套范例(凡是是匿名内部类)作为函数代码的替代。默认的Collection类库不会为这些代码提供任何利便,可变性的魔咒也始终存在。

Java 8的Lambda表达式没有神奇地转酿成函数式语言。相反,它的浸染仍是建设强制的强定名范例语言,但有更好的语法支持Lambda表达式函数文本。与此同时,Collection类库也获得了加强,答允Java开拓人员开始回收简朴的函数式气势气魄(譬喻filter和map)简化粗笨的代码。

Java 8需要引入一些新的范例来暗示函数管道的根基结构块,如java.util.function中的Predicate、Function和Consumer接口。这些新增的成果使Java 8可以或许“稍微函数式编程”,但Java需要用范例来暗示它们(而且它们位于东西类包,而不是语言焦点),这说明标明范例仍然束缚着Java语言,它离纯粹的Lisp方言可能其它函数式语言是何等的遥远。

除了以上这些,这个函数式语言能量的小荟萃很大概是所有大大都开拓者日常开拓所真正需要的。对付高级用户,尚有(JVM或其它平台)其它语言,而且毫无疑问,将继承发达成长。

    关键字:

在线提交作业