在本文中,我们将深入研究一个有用的概念,即同构关系,它通常在功能语言中使用,但在Java中很少见。 变形很容易实现,通常只需要几行Java代码。 那几行代码为您的内部API增添了强大的功能,有效地引入了编译器强制执行的详尽的结构模式匹配,这种模式可以消除令人沮丧的情况并阻止难以解决的错误(这些错误可能通过复杂的if / then / else意大利面条代码潜入)。
简单的概念,复杂的词
Catamorphism这个词是由希腊语中的向下(cata)和转换(morphism)两个词组成的,对于大多数Java开发人员来说,听起来似乎有些陌生。 维基百科上的定义是
在函数式编程中 , 类同态化将列表的折叠形式推广为任意代数数据类型,可以将其描述为初始代数
对大多数人来说可能是完全听不懂的。 遗憾的是,同构是一个非常有用且相当简单的概念,可用于生成既更干净(主观)又更安全/漏洞更少(客观)的代码。
Lambda访客模式
虽然“变形”一词可能尚未进入主流Java词典,但已将主体编纂为“四人帮”设计原则。 访客模式定义了实现变态的繁琐而复杂的方式(这可能就是为什么它未被广泛使用的原因)。 幸运的是,随着Java 8的到来,实现了访问者模式(和更广泛的分类)变得更加简单。
可选的同构
在实施变形时,目标是以安全,详尽的方式提供对某种类型的关键结构信息的访问。 Java 8的Optional类可以处于两种状态之一
呈现(带有一些数据)
空(无数据)
Optional的同构可以用两个函数来表示
Present:一个接受Optional值的juFunction
空:执行以获取值的juSupplier
变形的调用者提供两种函数类型的两种lambda(或其他实现),仅根据状态有选择地执行其中一种。
我们可以简单地为Optional定义催化形态
为了使用分类,我们提供了Optional来处理和执行我们的功能。 在下面的代码中,a将保存我们的供应商返回的值,b将执行我们的函数的结果(其中输入参数是可选10的值)。
可选的模式匹配
事实证明,当我们使用catamorphism作为Optional时,我们所做的事情与功能语言(以及Scala等双重范式语言)中的模式匹配非常相似。 也就是说,我们正在解构Optional实例,并根据其内部状态选择适当的大小写/函数来执行。
最终,在Option上进行模式匹配的Scala代码与Optional的访问者/同化代码非常相似。
为什么这样好?
Scala中的模式匹配不仅是Java的Switch语句的更强大版本。 Scala编译器还能够检测开发人员是否已经涵盖了解构中的所有可能情况,如果没有,则可以发出警告或错误。
类似地,如果Java开发人员使用了同构关系,则编译器将强制他们为Optional(或受审查的类型)具有的所有可能状态提供实现。
吸气剂破坏封装
Scala具有案例类的概念。 案例类是非常简单的类,具有不变的字段,可以非常简洁地定义它们。
我们可以在Java中定义一个类似的类,虽然类稍大一点,但也非常明确地表明了编译后的类结构实际上是什么。
创建后,用法非常相似
除了一个方面之外,在Book的无形Java实现中,我们可以在Scala中进行模式匹配,但不能在Java中进行模式匹配
但这很容易解决
在Java中,我们现在还有其他选择,可以将案例类成员设为私有,并强制所有访问都通过分类。 对于像Book示例这样的简单类,这样做的好处是微不足道的,但是正如我们在Optional中看到的那样,即使是一点点额外的复杂性也意味着错误可能会开始渗入我们的代码库中。 让我们通过引入Book的两个新子类-Fiction和Nonfiction来实际地看到这一点。
小说或非小说
我们可以重构Book类,进行一些更改
- 我们将Book设为抽象类,因为我们现在有两种具体类型(改为小说和非小说)
- 我们定义了两种子类型(小说和非小说)
- 我们将所有字段设为私有(以简化使用Book实例的代码!)
该类有一些值得注意的属性,可以定义的Book的唯一具体类型是Fiction和NonFiction。 这是由私有构造函数强制执行的。 另外,对Book实例的内部状态或结构的所有访问都通过我们原始的变形来控制。
我们如何不同地处理小说和非小说
现在我们有两种类型的Book,我们可能会想通过传统的Java if / then / else语句来处理它们。
更好的方法是定义另一个折叠形式,折叠在可能的Book类型上。 该实现与我们的Optional实现非常相似,不同之处在于我们将接受两个单参数函数。 一个将接收小说实例,而另一个将接收非小说实例。
如果/ then / else语句可以更安全地重写我们的越野车
Either(或Xor类型)的分解
我们刚刚得到的一本书,它们可以是小说还是非小说类的catamorphism。 可以将其通用化以说明可以是其他两种类型之一(例如,类型T1,类型T2)的类型。 表示这种情况的通用数据结构是Either类型。 (cyclops-react提供Either的延迟和响应式实现,最多可提供5种选择,以及一个渴望的数据结构版本,称为Xor-eXclusive or)。 就像当字段为空时我们如何使用Optional,而不是一致地重新实现表示继承层次结构一样,我们都可以利用现有的数据结构。
我们可以重构Book类以使用Either代替
在Book类型上进行模式匹配,我们可以使用match方法
列表的变态
当列表在功能上实现时,通常按照头和尾来构造(递归)
头:第一价值
尾巴:包含头尾的列表
列表的分类是由两个参数函数(或BiFunction)组成,该函数同时接受列表的第一个值和尾部(列表的其余部分)。 我们可以在cyclops-react中使用扩展列表类型(ListX)轻松实现此目的。 ListX可以包装任何标准的juList,并增加了许多有用的功能。 即使没有(通常)按照头和尾实现juLists,我们也可以抽象(或虚拟)地表示它们,并且仍然可以应用我们的简单分类方法。
通过使用Scala模式匹配,我们可以通过附加头部和递归处理尾部来将字符串列表折叠(或缩小)为单个字符串。
在Java中,使用List变形,我们基本上可以做同样的事情
实现自己的变形
在本文中,我们显示了对Java 8核心类型之一(可选)实施分类转换(或模式匹配)多么简单。 然后通过一个可行的示例,我们用Java自己的类同实现了一个非常简单的Case类。 我们展示了如何通过为Either类型实现催化同构来简化Java中的继承层次结构。 最终,我们实现了juList的功能同构,即使它没有公开头和尾的简单递归数据结构。
所有这些变形都用3行或更少的代码来实现,但是它们使我们能够在为其创建的类型的结构的重要方面进行模式匹配。 您也可以在自己的代码库中执行此操作。
下次当您发现自己陷入困境时,如果/ then / else sequence或case语句询问是否可以以某种方式简化,几乎可以肯定的是,关于对象结构的某些知识可以指导您创建简单的变形,从而提高鲁棒性您的代码。
From: https://hackernoon.com/catamorphisms-for-java-developers-e3cc10b43d03