2019独角兽企业重金招聘Python工程师标准>>>

在与 Martin Fowler 共同参加的一次主题演讲中,他提供了一个敏锐的观察报告:

Java 的遗产是  平台,不是  语言

最初的 Java 技术工程师曾做过一个了不起的决定,将语言从运行时中分离出来,最终使 200 多种语言可在 Java 平台上运行。该基础架构对平台保持长久活力非常关键,因为计算机编程语言的寿命通常很短。自 2008 年以来,每年由 Oracle 主办的 JVM 语言峰会都会为 JVM 上替代语言的实现者提供与平台工程师公开合作的机会。

欢迎来到 Java 下一代专栏系列。在这里,我将简要介绍三种现代 JVM 语言:Groovy、Scala 和 Clojure,它们将范式、设计选择和舒适因素进行了有趣的组合。我不打算在这里详细介绍每种语言;它们各自的网站上都有这方面的介绍(参阅 参考资料)。但语言社区网站(主要目的是福音传道)上没有提供语言不适应的客观信息或任务示例。在本系列文章中,我将进行实质性对比,帮助填补这项空白。本文准备概述 Java 下一代语言以及学习这些语言的好处。

超越 Java

Java 语言因 Bruce Tate 在其著作 超越 Java(参阅 参考资料)中将其称为 完美风暴而出名,导致 Java 出名的综合因素包括:Web 的兴起、现有 Web 技术因各种原因产生的不适应性,以及企业多层应用开发的兴起。Tate 也认为完美风暴是一系列独立事件,而其他语言不会以同样方式达到同样的高度。

Java 语言已证明其功能相当灵活,但人所共知,其语法和固有范式具有一定的局限性。尽管 Java 语言正在进行一些看似美好的改变,但其语法根本不支持一些重要的未来目标,如函数式编程元素。但是,如果您试图找到一种新语言取代 Java,那您就错了。

多语言编程

多语言编程是我在 2006 年的一片博客文章中重新提出并推广的一个术语(参阅 参考资料),多语言编程以单一语言并不适合解决所有问题的认知为基础的。一些语言具有更适合某些特定问题的内在特性。例如,虽然 Swing 与 Java 一样成熟,但开发人员发现在 Java 中编写 Swing UI 非常麻烦,因为它要求进行类型声明,要求行为具有匿名内部类,并且具有其他冲突因素。使用更适合构建 UI 的语言,比如带有SwingBuilder的 Groovy(参阅 参考资料),就会使构建 Swing 应用程序变得更容易。

JVM 上运行的语言的扩展使多语言编程的构思更具吸引力,因为您可以在维护相同的底层字节码和库时将其混搭。例如,SwingBuilder不能取代 Swing;它在现有 Swing API 上进行分层。当然,长期以来,开发人员一直混合使用 JVM 以外的语言(例如,混搭使用 SQL 和 JavaScript 来实现特定目的),这在 JVM 范围内更加普遍。许多 ThoughtWorks 项目包含多种语言,ThoughtWorks Studios 开发的所有工具均使用混合语言。

即使 Java 依旧是您的主要开发语言,也可以了解如何运行其他语言,以便在策略上使用它们。Java 依然是 JVM 生态系统的重要部分,但最终人们更倾向于将它用作平台汇编语言 —一个您可以完全了解性能或满足特定需求的地方。

发展

20 世纪 80 年代初,在我上大学期时,我们使用称为 Pecan Pascal 的开发环境。它的独特之处是可以同时在 Apple II 或 IBM PC 上运行相同的 Pascal 代码。Pecan 工程师使用某种称为 “字节码” 的神秘东西实现了这一壮举。开发人员将他们的 Pascal 代码编译成这种 “字节码”,并在为每个平台本地编写的 “虚拟机” 上运行。多么可怕的经历啊!甚至对于简单的任务而言,生成代码也极其缓慢。当时的硬件根本无法应对这种挑战。

发布 Pecan Pascal 之后的十年,Sun 发布了 Java,Java 使用了相同的架构,对于 20 世纪 90 年代中期的硬件环境,运行该代码显得有些紧张,但最终取得了成功。Java 还增加了其他开发人员友好的特性,如自动垃圾收集。使用过像 C++ 这样的语言之后,我再也不想在没有垃圾收集的语言中编写代码。我宁愿花将时间花在更高层次上的抽象上,思考解决复杂业务问题的方法,也不愿意在内存管理等复杂的管道问题上浪费时间。

计算机语言通常不会有很长的寿命,原因之一是语言和平台设计的创新速度。随着我们的平台逐渐强大,可以处理的繁重作业也就越多。例如,Groovy 的 备忘特性(2010 年增加的特性)缓冲了函数调用结果。不需要手写缓冲代码,这可能会引入 bug,只需调用 memoize()方法即可,如清单 1 所示:

清单 1. 在 Groovy 中备忘一个函数
def static sum = { number -> factorsOf(number).inject(0, {i, j -> i + j}) } def static sumOfFactors = sum.memoize()

清单 1 中,sumOfFactors方法的结果是自动缓存的。您也可以使用另一种方法自定义缓冲行为,比如 memoizeAtLeast()和memoizeAtMost()。Clojure 还提供了备忘功能,这对 Scala 的实现是无足轻重的。下一代语言(以及一些 Java 框架)中的高级特性(比如备忘功能)将逐渐找到它们进入 Java 语言的方法。Java 的下一个版本将增加高阶函数,使备忘功能的实现变得更容易。通过学习下一代 Java 语言,提前了解未来 Java 特性。

Groovy、Scala 和 Clojure

Groovy 是 21 世纪的 Java 语法(浓缩咖啡,而非普通咖啡)。Groovy 的设计目标是更新并减少 Java 语法阻力,同时支持 Java 语言中的主要范式。因此,Groovy 需要 “了解” JavaBeans 这类技术,并简化属性访问。Groovy 快速合并新特性,并提供了一些重要函数特性,我将在后面几期中重点介绍。Groovy 在根本上依然是面向对象的命令式语言。Groovy 与 Java 的两个主要区别是,Groovy 是 静态而非动态类型,而且它的元程序功能更佳。

Scala 是一种充分利用了 JVM 优势的语言,但其语法完全进行了重新设计。Scala 是一种强静态类型语言(比对类型要求比较严格的 Java 更严格)支持面向对象范式和函数范式,而且更青睐于后者。例如,Scala 倾向 val声明,并使不变的变量(类似于在 Java 中将参数标记为final)服从于 var,这创建了人们更加熟悉的可变变量。通过大力支持这两种范式,Scala 为您提供了从您可能是(一名面向对象的命令式程序员)到可能应该是(一名倾向函数式的程序员)的桥梁。

Clojure 是一种 Lisp 方言,在语法上彻底背离了其他语言。它是一种强动态类型语言(和 Groovy 一样),反映了专断的设计决策。虽然 Clojure 允许您用遗留 Java 进行完整和深入的交互操作,但它并不试图构建与旧式范式相连的桥梁。例如,Clojure 不具备纠错功能,并且支持面向对象进行交互操作。但是,它还支持对象程序员所习惯的所有特性,如多态性,但它以函数方式而非面向对象的方式来实现这些特性。Clojure 围绕一些核心工程原理(比如 Software Transactional Memory)进行设计,它打破了旧的范式,支持新的功能。

范式

除了语法之外,这些语言最有趣的不同之处在于类型和底层主要范式:函数式或命令式。

静态与动态类型

编程语言中的 静态类型指的是显式类型声明,如 Java 的 int x;声明。动态类型指的是不要求提供类型声明信息的语言。所有语言都是 类型语言,这意味着您的代码可以反映赋值后的类型。

Java 的类型系统饱受责备,因为它的静态类型带来了许多不便,而且无法获得较大收益。例如,在现行的有限类型推断之前,Java 要求开发人员在赋值等号的两侧重复进行类型声明。Scala 与 Java 相比是更加静态的类型,但在日常使用时并不麻烦,因为它很好地利用了类型推断。

Groovy 有一个行为,乍一看,该行为似乎架起了静态与动态之间的桥梁。请考虑清单 2 所示的简单集合工厂:

清单 2. Groovy 集合工厂
class CollectionFactory { def List getCollection(description) { if (description == "Array-like") new ArrayList() else if (description == "Stack-like") new Stack() } }

清单 2 中的类充当了一个工厂,它将根据传递的 description 参数返回两个 list 接口实现程序中的一个(ArrayList 或 Stack)。对 Java 开发人员而言,这似乎能够确保返回的结果满足规则。但是,清单 3 中的这两个单元测试显示了它带来的并发症:

清单 3. 测试 Groovy 中的集合类型
@Test void test_search() { List l = f.getCollection("Stack-like") assertTrue l instanceof java.util.Stack l.push("foo") assertThat l.size(), is(1) def r = l.search("foo") } @Test(expected=groovy.lang.MissingMethodException.class) void verify_that_typing_does_not_help() { List l = f.getCollection("Array-like") assertTrue l instanceof java.util.ArrayList l.add("foo") assertThat l.size(), is(1) def r = l.search("foo") }

在 清单 3 的第一个单元测试中,我通过工厂检索 Stack,验证它确实是 Stack,然后执行 Stack的操作,如 push()、size()和 search()。但在第二个单元测试中,我必须用 MissingMethodException 预期发生的异常,以保护测试,这样才能通过测试。在检索 Array-like 集合并将它作为一个 List 放入变量类型时,我可以证明我确实收到了一个 list。但是,当我试图调用这个 search()方法时,它会触发异常,因为ArrayList不包括 search()方法。因此,通过提供无编译时间保护的声明,可以确保方法调用是正确的。

虽然这看起来可能像一个 bug,但它却是正确的行为。Groovy 中的类型只确保 赋值语句的有效性。例如,在 清单 3 中,如果我返回一些 List接口的内容,则会触发运行时异常(GroovyCastException)。这会让 Groovy 与 Clojure 在强动态类型家族中牢牢站稳脚跟。

然而,语言最近发生的变化已使 Groovy 中的静态和动态区分变得十分模糊。Groovy 2.0 新增了一个 @TypeChecked注释,使您能够在类或方法级别上制定严格类型检查特别决策。清单 4 举例说明了这个注释:

清单 4. 类型检查与注释
@TypeChecked @Test void type_checking() { def f = new CollectionFactory() List l = f.getCollection("Stack-like") l.add("foo") def r = l.pop() assertEquals r, "foo"}

在 清单 4 中,我添加了 @TypeChecked 注释,验证了赋值和随后的方法调用。例如,清单 5 中的代码不再进行 编译

清单 5. 类型检查可防止无效的方法调用
@TypeChecked @Test void invalid_type() { def f = new CollectionFactory() Stack s = (Stack) f.getCollection("Stack-like") s.add("foo") def result = s.search("foo") }

在 清单 5 中,我必须为工厂返回添加类型转换,以支持我调用 Stack的 search()方法。该工具与限制条件一起使用:在启用静态类型时,Groovy 的许多动态特性是无效的。但是,该示例说明了 Groovy 在建立静态和动态划分之间的桥梁时的不断变化。

所有这些语言都是十分强大的元程序编程工具,所以事后可以添加更加严格的类型。例如,有一些辅助项目可以将选择类型添加到 Clojure。但是,通常情况下,如果选择类型是可选的,那么它就不再是类型系统的一部分;它是一种验证机制。

命令式与函数式

另一个主要比照是命令式与函数式。命令式编程主要关注分步指令,在许多情况下,模仿古老的低级硬件的便利条件。函数式编程更关注一流结构函数,并试图最大程度地减少状态转换和易变性。

受 Java 影响的比较大的 Groovy 实际上是一种命令式语言。但从一开始它就包含大量函数式语言特性,随着时间的推移,还在不断添加新的特性。

Scala 将这两种范式结合起来使用并为它们提供支持。虽然函数式编程更受喜欢(并受支持),但 Scala 仍然支持面向对象的编程和命令式编程。因此,恰当使用 Scala 要求一个纪律严明的团队确保您不会将范式随意混合搭配,这在多范式语言中一直是一种危险举动。

Clojure 没有提供纠错功能。它支持面向对象的编程,以便支持与其他 JVM 语言轻松交互,但它并不会充当桥梁。相反,Clojure 的专断设计声明设计师所思考的正是良好的工程实践。那些决策影响深远,使 Clojure 能够以开创性的方式解决 Java 世界冗繁的问题(如并发性)。

了解这些全新语言所需的诸多思维转换都来自命令式 / 函数式划分,而且这是本系列文章中最有意义的探索领域之一。

结束语

开发人员生活在一个采用多种语言解决问题的语言种类越来越多的世界中。学会有效使用新语言有助于您确定某种方法何时适用。您无需抛弃 Java,它将逐渐融合下一代 JVM 语言的特性;通过了解它们,您可以窥视 Java 语言的未来。

在下一期的 Java 下一代语言中,我将开始对比 Groovy、Scala 和 Clojure 之间的共同之处。

转载于:https://my.oschina.net/u/1032350/blog/336746

Java 下一代: Groovy、Scala 和 Clojure相关推荐

  1. Java程序员必备秘籍 Scala与Clojure函数式编程语言

    编程世界就好比江湖,各种技术与思想有如各种内外家功夫在历史的舞台上纷呈登场,各领风骚.如今,自C.C++传承而来的以Java为代表的命令式语言一派可谓如日中天.门徒万千.多年来,这几门语言一直占据着T ...

  2. java 下一代_Java 下一代: 混入和特征

    Java 语言的开发人员精通 C++ 和其他语言,包括多继承(multiple inheritance),使得类可以继承自任意数量的父类.多继承带来的一个问题是,不可能确定所继承的功能来自哪个父类.这 ...

  3. windows通过脚本批量设置环境变量(env、path)实战:java环境、scala环境、maven环境、gradle环境、nodejs、git等

    windows通过脚本批量设置环境变量(env.path)实战:java环境.scala环境.maven环境.gradle环境.nodejs.git等 目录

  4. Java 8 vs. Scala之Lambda表达式

    [编者的话]2014年3月份众人期待已久的Java 8发布了,新版本从语言.编译器.类库和工具等方面对Java进行了诸多改进与提升,一时间风光无限:而JVM体系的另一门语言Scala则因为融合了函数式 ...

  5. 修改Intellij IDEA中工程对应的Java SDK、Scala SDK

    如果编译Scala工程时,遇到如下异常: can't expand macros compiled by previous versions of Scala 很可能是工程的scala版本,和依赖的包 ...

  6. groovy 使用java类_在java中使用groovy怎么搞 (java and groovy)

    什么是groovy? 一种基于Java虚拟机的动态语言,可以和java无缝集成,正是这个特性,很多时候把二者同时使用,把groovy作为java的有效补充.对于Java程序员来说,学习成本几乎为零.同 ...

  7. 【Groovy】Groovy 语言特点简介 ( 支持 Java 语法 | 支持 Java 虚拟机 | Groovy 语言是动态语言 | Groovy 扩展 JDK | 编译时元编程 )

    文章目录 一.Groovy 支持 Java 语法 二.Groovy 支持 Java 虚拟机 三.Groovy 语言是 动态语言 四.Groovy 扩展 JDK 五.Groovy 编译时元编程 一.Gr ...

  8. java.lang.NoSuchMethodError: scala.Predef$.refArrayOps([Ljava/lang/Object;)[Ljava/lang/Object解决

    完整报错如下: 19/05/15 15:26:10 WARN BLAS: Failed to load implementation from: com.github.fommil.netlib.Na ...

  9. scala to java_Scala 2.13 以后Java集合与Scala集合互相转换

    原来需要Java集合和Scala集合类型互换时引入import scala.collection.JavaConverters._1 即可,但在Scala 2.13版本中JavaConverter已经 ...

  10. scala 异步调用_非阻塞异步Java 8和Scala的Try / Success / Failure

    scala 异步调用 受Heinz Kabutz最近的时事通讯以及我在最近的书中研究的Scala的期货的启发,我着手使用Java 8编写了一个示例,该示例如何将工作提交给执行服务并异步地响应其结果,并 ...

最新文章

  1. 时间罗盘html源代码_重磅!Vue 3.0源代码公布后,究竟有哪些变更?
  2. linux 限制单个ip流量,centos 的單ip流量控制-CentOS下利用iptables限速及限制每IP連接數...
  3. 实现一个正则表达式引擎in Python(三)
  4. 莓良心(第二类斯特林数)
  5. 服务器时间维护制度,网络设备及服务器日常维护管理制度
  6. vue 列表渲染 v-for
  7. sqlite的编译、练习
  8. 20190820:(leetcode习题)有效的字母异位词
  9. CSS3 transform-style 属性
  10. hp服务器风扇告警信息,HP ProLiant 服务器 - POST 错误消息和蜂鸣代码
  11. 计算机键盘指示灯不亮也不启动不了机,电脑开不开机,显示器无反应,键盘指示灯不亮,主...
  12. 高仿京东分类页面实现
  13. 基于python的智能风扇设计_基于单片机的智能风扇的设计与实现
  14. JQ...CSS3爱心飘落特效
  15. 【外文翻译】图像中的傅里叶变换
  16. 微服务架构-分布式解决方案-110:基于canal整合kafka异步解决数据一致性的问题
  17. 每日加瓦,终成栋房3
  18. Hibernate对象状态
  19. nrf51822资料
  20. matlab如何读取.mat文件,matlab中读取mat文件

热门文章

  1. 智能优化算法:群居蜘蛛优化算法-附代码
  2. 【Tensorflow】tensorboard小结
  3. Python-正则表达式提取想要的内容
  4. python绘图坐标轴|刻度值|刻度||标题设置
  5. 地图Web服务API接口——搜索POI(以高德地图为例)
  6. Java中数字朝着0.5的倍数取舍
  7. 实习踩坑之路:Mybatis写的sql语句有<符号的问题导致项目启动失败以及count(*)怎么对应到某个实体类的字段
  8. 阿里内部员工,排查Java问题常用的工具单
  9. 在钢筋混泥土的城市,打铁还需身体硬
  10. linux的tar命令压缩26g文件,如何使用Linux上的tar命令压缩和解压缩文件 | MOS86