Java records

详细了解 Java 记录,以及做出错误决策的容易程度。 

有一个官方的 JEP 359: 记录(预览版)建议在Java中引入“记录”,即纯数据结构。有了它,Java继续跟随其他当代语言进入“多范式”编程领域。

本文探讨了这种变化对面向对象、Java 开发和我们整个行业意味着什么。

旧名称,旧功能

首先,“记录”既不新鲜也不现代——实际上恰恰相反。记录和结构在编程中一直存在;它们在 60 年代出现在 COBOL 中,后来出现在广泛使用的语言中,如 Pascal、C 等。

这些语言遵循过程 编程范式,这意味着数据被建模为纯数据,然后有一些 过程 ,其中“业务逻辑”是通过操作作为参数传递的数据来实现的,全局可用或从其他地方获得。


然后面向对象和Smalltalk在70年代和80年代出现,它取消了纯数据结构,转而支持对象 ,对象 被认为是独立的代理,与消息合作和通信以实现请求的逻辑。即使在 Java 诞生的 90 年代,这个原始的想法仍然非常强烈,以至于 Java 最初没有引入纯数据结构。

然后,我们的行业在 90 年代后期几乎爆炸式增长;“电子商务”现在是一个东西。Java推出了“企业版”,对Java/JavaEE开发人员的需求巨大。这就是向面向对象过渡时开始出现裂缝的地方。没有足够的面向对象开发人员,所以大多数人(包括我自己)都来自过程背景。在我担任初级开发人员的团队中,没有人解释面向对象应该如何工作,所以我们基本上继续用Java生成C代码。我们没有使用数据结构,而是使用“Beans”,而不是过程,我们使用笨拙地分组到随机类(通常称为管理器)中的方法。当时的大多数项目都是这样运作的。

从那以后的20年里,这个基本设计没有太大变化。分层架构在 90 年代已经是一件事情,今天仍然很常见。将数据模型(现在有时称为“域模型”)与逻辑分开仍然是迄今为止Java软件开发的主要方法

过去的情况是否更好,程序方法是否成功?

我们行业的现状


为了完成我们的历史视角,更详细地了解我们现在所处的位置可能是有益的。我无法比 更简洁地总结这一点。

只是为了留在“企业”领域,业务和发展之间存在对抗关系。不是在人们之间,而是在区域之间。开发通常根本不是业务的一部分,它们是企业不情愿保留甚至外包的“服务”。一般来说,开发和 IT 是一个“成本中心”。这是MBA对一件只花钱但被认为没有用的东西的谈话。

事情并非总是这样。即使在90年代,商业人士也很高兴与我们合作,告诉我们他们是如何运作的,并要求我们帮助他们为客户提出新的想法。我们与他们并肩工作,不仅开发软件,而且作为业务运营的一部分。人们信任我们,因为我们一直在那里,看到他们发生了什么,以及他们是如何工作的。我们几乎每周都会提出一些小的增强功能。我们也没有单独预算,而是与业务组一起工作。可以说,我们帮助赚钱,而不仅仅是烧掉它。

那么发生了什么变化呢?嗯,很多事情,但最重要的是,我们在工作中做得不够好。事情花了太长时间来构建,它们没有按预期工作,而且随着代码库的增长,事情只会变得更糟。然后,企业开始把我们推开,要求有某种形式的合同担保,这导致信任和沟通减少,所以螺旋式上升。

尽管发生了这些变化,但我们作为一个行业,继续以同样的方式做事。我们仍然以同样的方式思考,我们仍然主要使用相同的,本质上是程序化的方法来进行软件设计,并进行一些小的装饰性更改。因此,尽管Java确实试图向面向对象迈出一步,但现在似乎它承认它在很大程度上是不成功的,并且它正式恢复到更流行的过程方法,从历史的角度来看,我们已经知道它并不真正有效。那么Java为什么要这样做呢?

动机和目标

提案中给出的拥有记录的主要原因是“将数据建模为数据”,如下所示:

record Point(int x, int y) {
}

让我们务实地看一下这种方法。这段代码的可读性和可维护性如何?让我们问一些问题:

  • 应用程序用它做什么?
  • 我可以将 更改为 int long
  • 我可以将笛卡尔坐标系更改为极坐标吗?
  • 极坐标会是更优化的表示形式吗?
  • 如果我使用坐标将其z扩展到 Point 3D,我必须更改什么?

这些只是我们在查看时可能感兴趣的一些事情 Point

所以取而代之的是,这个怎么样:

public final class Point {
   private final int x;
   private final int y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }

   public Line connect(Point other) {
      ...
   }

   public Line throughAt(double angle) {
      ...
   }

   public Circle circle(int radius) {
      ...
   }
}

这显然是更多的代码,但也有更多的信息。我们知道它的 Point 用途和用途。我们看到内部表示保持内部,因此应用程序无法以此处未定义的其他方式使用 。 Point 我们也可以自由地改变这个内部表示,看看根据这里存在的逻辑,极坐标是否会更好,等等。我们不必去别处寻找来弄清楚这一切。

那么哪个更“可读”呢?哪个传达了更多信息?哪个更易于维护?哪个可以保证正确使用?哪个可以少被滥用?

当然,现实情况是,与Java中传统的纯数据“对象”相比,所提出的“记录”更具可读性,但该提案完全忽略了从历史,可维护性,可读性和设计的角度来看,该方法本身可能是错误的。

函数式编程


有一些论点表明,记录不仅仅是来自C的只读“结构”,实际上是 来自函数式编程的“代数数据类型”。而且因为“代数”和“函数”这两个词听起来很酷,所以它一定很好。

函数式编程的核心是软件应该是纯数学函数的组合。接收一些值(即不可变数据)并始终为相同参数生成相同值的函数。Java甚至没有不可变数据的概念,也没有纯函数的概念,也没有适当的函数组合工具。即使是建议的记录也只是浅层不可变的,这意味着它们可以引用其他可变的对象。

即使忽略所有这些,Java中实际的实际现实设计显然不是功能性的,而主要是过程性的。因此,很明显,记录将用于支持当前设计,无论其目的是什么。

您不必使用它

必须解决这个论点,因为这是对新功能批评的频繁回应: 好吧,如果你不想使用它,你不必使用它。

当然,这完全忽略了我们没有人孤立地工作。我们与同事、外部合作伙伴、顾问甚至我们从未见过的人一起工作。语言支持的任何内容 都将 被使用, 每个人都 必须处理它。

此外,语言的特征表明用法,这是设计和使用某些解决方案的惯用方式。因此,建议可以忽略语言功能是不现实的。 这是 60 年代的范式,它部分负责 90 年代和 2000 年代软件开发的“失宠”。这是一个信号,表明向面向对象的过渡,即Java在90年代开始的有可能解决程序世界问题的范式基本上被放弃了。

记录是功能范式的一部分的概念是一个红鲱鱼。Java不是,也从来不是函数式的,现实生活中的Java代码几乎是程序化的。

因此,虽然“记录”似乎受到普遍欢迎,但人们,尤其是致力于面向对象范式的开发人员,在考虑这个新功能时应该格外小心,甚至避免完全使用它。

延伸阅读

Java面向未来的项目

Comments are closed.