Java函数式思维: 函数设计模式

尽管 Java 不支持这些技术,下一代 JVM 语言均支持这些技术,但其具体实现细则有所不同。在本文中,Neal Ford 将探讨 Groovy、">Scala 和 Clojure 如何通过以 Java 无法支持的方式来实现函数式扩展,从而实现解释器设计模式的目的。

在本期 函数式思维 的文章中,我将继续研究 Gang of Four (GoF) 设计模式(参阅 参考资料)的函数式替代解决方案。在本文中,我将研究最少人了解,但却是最强大的模式之一:解释器 (Interpreter)。

解释器的定义是:

给定一个语言,定义其语法表示,以及一个使用该表示来解释语言中的句子的解释器。

换句话说,如果您正在使用的语言不适用于解决问题,那么用它来构建一个适用的语言。关于该方法的一个很好的示例出现在 Web 框架中,如 Grails 和 Ruby on Rails,它们扩展了自己的基础语言(分别是 Groovy 和 Ruby),使编写 Web 应用程序变得更容易。

这种模式最少人了解,因为构建一种新的语言并不常见,需要专业的技能和惯用语法。它是最强大的 设计模式,因为它鼓励您针对正在解决的问题扩展自己的编程语言。这在 Lisp(因此 Clojure 也同样)世界是一个普遍的特质,但在主流语言中不太常见。

当使用禁止对语言本身进行扩展的语言(如 Java)时,开发人员往往将自己的思维塑造成该语言的语法;这是您的惟一选择。然而,当您渐渐习惯使用允许轻松扩展的语言时,您就会开始将语言折向解决问题的方向,而不是其他折衷的方式。

Java 缺乏直观的语言扩展机制,除非您求助于面向方面的编程。然而,下一代的 JVM 语言(Groovy、Scala 和 Clojure)均支持以多种方式进行扩展。通过这样做,它们可以达到解释器设计模式的目的。首先,我将展示如何使用这三种语言实现操作符重载,然后演示 Groovy 和 Scala 如何让您扩展现有的类。

操作符重载(operator overloading)

操作符重载 是函数式语言的一个常见特性,能够重定义操作符(如 +、- 或 *)配合新的类型工作,并表现出新的行为。操作符重载的缺失是 Java 形成时期的一个有意识的决定,但现在几乎每一个现代语言都具备这个特性,包括在 JVM 上 Java 的天然接班人。

Groovy

Groovy 尝试更新 Java 的语法,使其跟上潮流,同时保留其自然语义。因此,Groovy 通过将操作符自动映射到方法名称实现操作符重载。例如,如果您想重载 Integer 的 + 操作符,那么您要重写 Integer 类的 plus() 方法。完整的映射列表已在线提供;表 1 显示了列表的一部分:

表 1. Groovy 的操作符/方法映射列表的一部分

操作符 方法 x + y x.plus(y) x * y x.multiply(y) x / y x.div(y) x ** y x.power(y)

作为一个操作符重载的示例,我将在 Groovy 和 Scala 中都创建一个 ComplexNumber 类。复数 是一个数学概念,由一个实数 和虚数 部分组成,一般写法是,例如 3 + 4i。复数在许多科学领域中都很常用,包括工程学、物理学、电磁学和混沌理论。开发人员在编写这些领域的应用程序时,大大受益于能够创建反映其问题域的操作符。

清单 1 中显示了一个 Groovy ComplexNumber 类:

清单 1. Groovy 中的 ComplexNumber

package complexnumsclass ComplexNumber { def real, imaginary public ComplexNumber(real, imaginary) { this.real = real this.imaginary = imaginary } def plus(rhs) { new ComplexNumber(this.real + rhs.real, this.imaginary + rhs.imaginary) } def multiply(rhs) { new ComplexNumber( real * rhs.real - imaginary * rhs.imaginary, real * rhs.imaginary + imaginary * rhs.real) } String toString() { real.toString() + ((imaginary < 0 ? "" : "+") + imaginary + "i").toString() }}

在 清单 1 中,我创建一个类,保存实数和虚数部分,并且我创建重载的 plus() 和 multiply() 操作符。两个复数的相加是非常直观的:plus() 操作符将两个数各自的实数和虚数分别进行相加,并产生结果。两个复数的相乘需要以下公式:

(x + yi)(u + vi) = (xu - yv) + (xv + yu)i

在 清单 1 中的 multiply() 操作符复制该公式。它将两个数字的实数部分相乘,然后减去虚数部分相乘的积,再加上实数和虚数分别彼此相乘的积。

时间: 2024-10-28 10:52:42

Java函数式思维: 函数设计模式的相关文章

Java函数式编程(一):你好,Lambda表达式_java

第一章 你好,lambda表达式! 第一节 Java的编码风格正面临着翻天覆地的变化. 我们每天的工作将会变成更简单方便,更富表现力.Java这种新的编程方式早在数十年前就已经出现在别的编程语言里面了.这些新特性引入Java后,我们可以写出更简洁,优雅,表达性更强,错误更少的代码.我们可以用更少的代码来实现各种策略和设计模式. 在本书中我们将通过日常编程中的一些例子来探索函数式风格的编程.在使用这种全新的优雅的方式进行设计编码之前,我们先来看下它到底好在哪里. 改变了你的思考方式 命令式风格--

函数式思维:大量转换:同义词掩盖了相似性

函数式编程语言实现代码重用的方法与面向对象的语言不同,这个主题我在 "第 2 部分" 中进行了分析.面向对象的 语言往往拥有众多可进行多种操作的数据结构,而函数式语言却只有极少数可进行多种操作的数据结构.面向对象的语言鼓 励您创建特定于类的方法,而您可以捕获一些重复出现的模式,以便以后重用.函数式语言鼓励您将常见转换应用于数据结 构,使用更高级的函数来定制特定实例的操作,从而帮助您实现重用. 相同的数据结构和操作出现在所有函数式语 言中(也出现在支持 Java 中的函数式编程的众多框架

java新手 主函数里面能创建静态方法吗

问题描述 java新手 主函数里面能创建静态方法吗 题目要求: 有一个抽象类ObjectVolume,如下所示: abstract class ObjectVolume { abstract double getVolume(); //返回物体的体积 } (1)编写球体类,立方体类和圆柱体类,它们是抽象类ObjectVolume的子类. (2)编写一个公共类,其中包含main方法和static void get(ObjectVolume obj)方法. (3)在main方法中,从键盘上输入1(表

Java函数式开发 Optional空指针处理_java

摘要 空闲时会抽空学习同在jvm上运行的Groovy和Scala,发现他们对null的处理比早期版本Java慎重很多.在Java8中,Optional为函数式编程的null处理给出了非常优雅的解决方案.本文将说明长久以来Java中对null的蹩脚处理,然后介绍使用Optional来实现Java函数式编程. 那些年困扰着我们的null 在Java江湖流传着这样一个传说:直到真正了解了空指针异常,才能算一名合格的Java开发人员.在我们逼格闪闪的java码字符生涯中,每天都会遇到各种null的处理,

从追MM谈Java的23种设计模式

Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 从追MM 谈Java 的23 种设计模式   1 .FACTORY- 追MM 少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM 爱吃的东西,虽然口味有所不同,但不管你带MM 去麦当劳或肯 德基,只管向服务员说" 来四个鸡翅" 就行了.麦当劳和肯德基就是生产鸡翅的Factory.   工厂模式:客户类和工厂类分开.消费者任

不用java的系统函数,把char转换成double

问题描述 不用java的系统函数,把char转换成double 就是,不用java的系统函数,把char转换成double,java菜鸟求帮忙- 解决方案 额,我初学还请指教,代码不写了感觉原理是这样:每次从字符串取一个字符,ch-'0'表示一位数字,然后d=10d+t,是不是这样呢 解决方案二: double优先级高于char,直接转:char a = 'a';double b = (double)a; 输出b应该是97.0:这个输出的是对应的ASCii码:如果用Double.parseDou

playsoundeffect-audioservice.java中playSoundEffect()函数里 关于形参的含义

问题描述 audioservice.java中playSoundEffect()函数里 关于形参的含义 onplaySoundEffect(int effectType ,int volume)函数里 关于参数int effectType ,int volume 代表的是什么意思,volume在log上显示值一直都为-1000,以及volFloat值代表的是百分比吗?它的计算过程能详细说明下吗?"volFloat = (float)Math.pow(10, (float)sSoundEffect

对象-java中compareTo函数可以在同一个类中重写多次吗?

问题描述 java中compareTo函数可以在同一个类中重写多次吗? 我有一个对象数组,想按照不同属性对这个数组进行排序.但是如果重写compareTo函数的话好像只能按照某一个属性进行排序.现在我既想按照年龄又想按照分数排序,那应该怎么写呢? 解决方案 不行,一个Comparable对应一种顺序标准,你这样按年龄又按分数,明显是两个定制顺序,结果自然是两种.建议要么合成一个逻辑:要么用两个集合来存放.

java中的函数的调用问题

问题描述 java中的函数的调用问题 新人初学java,有这样一个比较弱智的问题一直没有搞懂 我不知道在一个类中的函数方法能不能互相调用 public class A{ void b(){} void a(){ b(): } 我不知道这样对不对,麻烦各位了,谢谢 } 解决方案 这样是可以,类中的函数可以互调,对所以可见的函数都可以互调. 解决方案二: 可以的,方法本身也可以调用自己 解决方案三: 现在有一个Java函数,如下:......答案就在这里:请教c++中调用Java函数问题 解决方案四