>  기사  >  Java  >  Java 8의 함수형 프로그래밍에 대한 간략한 토론

Java 8의 함수형 프로그래밍에 대한 간략한 토론

伊谢尔伦
伊谢尔伦원래의
2016-11-26 10:55:011193검색

"Java 8은 Java에 함수형 프로그래밍을 제공합니다"에 대해 많은 논의가 있었지만 이 말이 실제로 의미하는 것은 무엇입니까?

이 기사에서는 기능주의와 그것이 언어 또는 프로그래밍 방식으로 의미하는 바에 대해 논의합니다. “Java 8의 함수형 프로그래밍은 어떤가요?”에 답하기 전에 Java의 진화, 특히 유형 시스템을 살펴보겠습니다. Java 8의 새로운 기능, 특히 Lambda 표현식이 Java의 환경을 어떻게 변화시켰는지 살펴보겠습니다. 그리고 함수형 프로그래밍 스타일의 주요 장점을 제공합니다.

함수형 프로그래밍 언어란 무엇인가요?

함수형 프로그래밍 언어의 핵심은 데이터를 처리하는 것과 동일한 방식으로 코드를 처리한다는 것입니다. 이는 함수가 일급 값이어야 하며 변수에 할당되거나 함수에 전달될 수 있어야 함을 의미합니다.

사실 많은 함수형 언어는 이보다 더 나아가 계산과 알고리즘을 연산하는 데이터보다 더 중요하게 취급합니다. 이러한 언어 중 일부는 프로그램 상태와 기능을 분리하기를 원합니다(보통 객체 지향 언어와는 다소 상반되는 방식으로 서로 더 밀접하게 연결됨).

Clojure 프로그래밍 언어는 클래스 기반 Java 가상 머신에서 실행되지만 본질적으로 기능적 언어이며 고급 언어 소스 프로그램에서 클래스와 객체를 직접 노출하지 않습니다. Java와의 우수한 상호 운용성을 제공합니다.

아래는 로그를 처리하는 데 사용되는 Clojure 함수이며, 클래스에 바인딩될 필요는 없습니다.

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

프로그램이 함수로 작성되면 프로그램의 다른 상태에 관계없이 주어진 입력에 대해 항상 동일한 출력을 반환하며 다른 효과가 없거나 프로그램 상태가 변경되지 않습니다. 이때가 함수형 프로그래밍이 가장 유용한 때입니다. 이는 수학 함수와 동일하게 동작하며 이 기준을 따르는 함수를 "순수" 함수라고도 합니다.

순수 함수의 가장 큰 장점은 작업이 외부 상태에 의존하지 않기 때문에 추론하기가 더 쉽다는 것입니다. 기능은 쉽게 결합될 수 있는데, 이는 Lisp 방언과 강력한 기능적 전통을 가진 다른 언어에서 흔히 사용되는 REPL(읽기, 실행, 인쇄, 루프) 스타일과 같은 개발자 워크플로 스타일에서 일반적입니다.

비함수형 프로그래밍 언어에서의 함수형 프로그래밍 ​​

언어가 함수형인지 아닌지는 양자택일의 문제가 아닙니다. 사실 언어는 그래프 위에 존재합니다. 마지막에는 함수형 프로그래밍이 기본적으로 시행되며 종종 변경 가능한 데이터 구조를 금지합니다. Clojure는 변경 가능한 데이터를 허용하지 않는 언어입니다.

그러나 일반적으로 프로그래밍이 함수형 방식으로 이루어지지만 언어에서는 이를 강제하지 않는 다른 언어도 있습니다. 스칼라는 하이브리드 객체지향 및 함수형 언어의 한 예입니다. 함수를 값으로 허용합니다(예:

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

). 동시에 Java와 매우 유사한 클래스 및 객체 구문을 유지합니다.

물론, 적절한 프로그래머 지침과 규칙이 유지되는 한, C와 같이 완전히 비함수적 언어로 함수형 프로그래밍을 수행하는 것도 가능합니다.

이를 염두에 두고 함수형 프로그래밍은 두 가지 요소의 함수로 보아야 합니다. 그 중 하나는 프로그래밍 언어와 관련이 있고 다른 하나는 해당 언어로 작성된 프로그램입니다.

1 ) 기본 프로그래밍 언어는 함수형 프로그래밍을 어느 정도 지원하거나 강제합니까?

2) 이 특정 프로그램은 언어가 제공하는 기능을 어떻게 사용합니까? 변경 가능한 상태와 같은 비기능적 기능을 방지합니까?

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}

记住,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,相反地,不支持这样的类型(除了几个稍微怪异的边缘例子)。

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!"); };

右侧的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或其它平台)其它语言,并且毫无疑问,将继续蓬勃发展。


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.