反思异常、表达式和空
在 上一期文章 中,我介绍了 Java 下一代语言用来消除 Java 语言中华而不实的东西和复杂性的创新方式。在这一期 文章中,我将展示这些语言如何消除 Java 的一些瑕疵:异常、语句与表达式,以及围绕 null 的边缘情况。
表达式
Java 语言从 C 语言那里继承的一项传承是区分编程语言 和编程表达式。Java 语句的示例包 括使用 if 或 while 的代码行,以及使用 void 来声明不会返回任何值的方法的代码行。表达式(比如 1 + 2 )用于求取 某一个值。
这种区分在最早的编程语言中就已经开始,比如在 Fortran 中,这种区分基于硬件条件以及对编程语言 设计的初步了解。在许多语言中,它被保留为操作(语句)与求值(表达式)的指示器。但语言设计人员逐渐意识到,该语 言可以完全由表达式组成,在对结果不感兴趣的时候忽略结果。事实上,所有函数式语言完全可以消除这种区分,仅使用表 达式。
Groovy 的 if 和 ?:
在 Java 下一代语言中,传统的命令式语言(Groovy)和函数式语言(Clojure 和 Scala)之间的分离展示了向表达式的进化。Groovy 仍然包含语句,这些语句基于 Java 语法,但添加了更多的表达式 。而 Scala 和 Clojure 则完全使用表达式。
语句和表达式中包含的内容都为语言增添了语法上的笨拙。可以考虑 Groovy 中的 if 语句,它继承自 Java。它有两个版本,清单 1 对它们进行了比较,这两个版本是用于执行判断的 if 语 句,以及神秘的三元运算符 ?::
清单 1. Groovy 的两个 if 语句
def x = 5 def y = 0 if (x % 2 == 0) y = x * 2 else y = x - 1 println y // 4 y = x % 2 == 0 ? (x *= 2) : (x -= 1) println y // 4
在 if 语句 清单 1 中,我必须将 x 的值设置为一个副作用 (side effect),因为 if 语句没 有返回任何值。要执行判断并同时进行赋值,必须使用三元赋值,如 清单 1 中的第二个赋值语句所示。
Scala 的 基于表达式的 if 语句
Scala 消除了对三元运算符的需求,允许 if 表达式对两种情况都进行处理。您可以使用它 ,就像在 Java 代码中使用 if 语句那样(忽略返回值),或者在赋值语句中使用它,如清单 2 中所示:
清单 2. Scala 的基于表达式的 if 语句
val x = 5 val y = if (x % 2 == 0) x * 2 else x - 1 println(y)
Scala 和其他两种 Java 下一代语言一样,不要求方法中包含明确的 return 语句。因此,方法的最 后一行是返回值,强调了这些语言中的方法的基于表达式的特性。
当您在 Java 和 Groovy 代码中进行操作和设置 值时,可以将每个响应封装为一个代码块,如清单 3 中所示,并包含任何所需的副作用:
清单 3. Scala if + 副 作用
val z = if (x % 2 == 0) { println("centerisible by 2") x * 2 } else { println("not centerisible by 2; odd") x - 1 } println(z)
在 清单 3 中,除了返回新计算得出的值之外,我还为每种情况打印了一条状态消息。代码块中的代 码行的顺序非常重要:代码块的最后一行表示符合条件的返回值。因此,当您使用基于表达式的 if 进行混合求值和具有副 作用时,必须非常小心。
Clojure 的表达式和副作用
Clojure 也完全由表达式组成,但它更进一步,从求值 代码中区分出了副作用代码。前两个示例的 Clojure 版本是用一个 let 代码块表达的,在清单 4 中,这允许定义局部作 用域变量:
清单 4. Clojure 的基于表达式的 if 语句
(let [x 5 y (if (= 0 (rem x 2)) (* x 2) (- x 1))] (println y))
在 清单 4 中,我为 x 分配了一个值 5,然后使用 if 建立了表达式来计算两个条件:(rem x2 ) 调用了 remainder 函数,类似于 Java % 操作符,并将结果与零值进行比较,在除以 2 时检查零剩余值(zero remainder)。在 Clojure 的 if 表达式中,第一个参数是 condition,第二个参数是 true 分支,第三个参数是可选的 else 分支。if 表达式的结果被分配给 y,然后被打印出来。
Clojure 也允许对每个条件使用代码块(可以包含副 作用),但需要一个包装器,比如 (do ...)。包装器通过使用最后一行作为代码块的返回值,对代码块中的每个表达式 进行求值。清单 5 说明了如何对某个条件或副作用进行求值:
清单 5. Clojure 中的显式副作用
(let [x 5 a (if (= 0 (rem x 2)) (do (println "centerisible by 2") (* x 2)) (do (println "not centerisible by 2; odd") (- x 1)))] (println a))
在 清单 5,我为 if 表达式的返回值分配了 a。对于每个条件,都创建了一个 (do ...) 包装 器,并允许使用任意数量的语句。代码块的最后一行是 (do...) 代码块的返回值,这类似于 清单 3 中的 Scala 示例。请确保目标返回值是最后进行求值的。以这种方式使用 (do...) 代码块是如此之常见,以致于 Clojure 中的许多 结构(比如 (let []))已经包含隐式 (do ...) 代码块,这消除了许多情况下对它们的需求。
Java/Groovy 代 码和 Scala/Clojure 代码中的表达式的比较指示了编程语言中的总趋势,即消除不必要的语句/表达式分歧。
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索语言
, java继承问题求解答
, 代码
, 清单
, clojure
, java方法块
, 表达式
, 语句
, java代码求讲解
, 三元一次式
, c语言表达式求值
java中if语句
scala groovy clojure、java scala groovy、groovy clojure、clojure scala、clojure和scala,以便于您获取更多的相关知识。