Java早期草案:不变对象的功能转换

2021-04-05 23:59:59

本文档是一个早期阶段草案,概述了在Java语言中支持功能转换的可能方向。这是一个探索文件,而不是构成任何特定版本的Java语言的任何特定功能的计划。本文件还可以参考探索下的其他特征;这纯粹是为了说明目的,并且不构成提供任何这些功能的计划或承诺。

每个人都喜欢记录。但是,添加了记录和解构等功能,可以解决一些旧问题,提高了新闻。而且,我们可以提供的一切,但没有课程的一切都会增加一个差距,用户可能会觉得他们努力选择;它的时候开始绘制概括记录的路径和#39;好吃的东西"这样合适的课程可以加入乐趣。

本文件主要侧重于一个特定的好代教,我们喜欢终止记录,然后尽快延伸到类:functionaltransformation。有其他潜在的新好东西(例如,基于关键字,基于关键字的建筑和解构)和新的善世的民主化opportonities(例如,任意课程的受访者)可以稍后。

更一般地说,在过去的10-15年里,Java开发人员已经开始达到不可抗性的价值,以创造安全,可靠的代码,这意味着它的理由,而且语言开始反映这种升值 - 但每次转弯,而是每次转弯和运行时仍然让我们为使用immutable对象征税。功能转换允许不可阻力的物体到无可用的可用性税(以及内联课程,也是较少的绩效税。)

记录和内联课是两种新形式的浅不变的课程戒律。这使得它更加明显,"突变" (应用功能转发到)一个不可变的物体目前过于痛苦。 (显然records不能突变 - 但是下一个最好的事情是创建一个新的录音,从现有记录中有一个已知的三角洲。)如果我们的点记录想要欺骗" set" x和y组件,它必须写入withx和携带方法:

记录点(int x,int y){点用x(int newx){返回新点(newx,y); }点富有(int newy){返回新点(x,newy); }}

这是可行的,并且具有与我们的语言合作的优势,但有两个明显的缺点。开发人员对通常的国家相关的样品板块令人难以令人兴奋,这将形成一个新的Theperplate,没有自动化的水位板(前进的两个步骤,一个退台),并且当记录有许多状态组件时,写作这些&#34 ;枯萎"变得更加繁琐和容易出错。对于记录,至少,语言已有信息来自动化,所以尤其是令人羞耻的开发人员用手做。

事实上,"枯萎"会比Getters和Setter更糟糕,因为虽然ACLASS可能有O(n)吸气器,但它可以想到的是o(2 ^ n)枯萎.worse,随着组件的数量增长,这些&#34的尸体;枯萎病#34;存取驻宿程序更容易出错。让' s在芽之前将此扼杀一个"模式" (或更糟糕的是,A"最佳实践")

这个问题不是记录或内联类的唯一;现有的基于价值(例如LocalDateTeme)必须揭示富于方法。但是如果语言会鼓励我们编写不可变类,它也会帮助我们解决这个问题。

我们在C#世界中的朋友已经在这个问题上拍摄了两个摇摆.TheIR第一个解决方案构建了它们已经允许参数允许参数默认值,后来添加了默认值的能力来引用它。这意味着您可以编写一个调用的普通库方法:

课程点{int x; int y;点(int x = this.x,int y = this.y){返回新点(x,y); }}

这是一个改进,它允许您编写一个方法来处理2 ^ n可能的组合,作为纯API考虑,并且客户端只能只能识别他们想要更改的参数:

但是,显然是C#开发人员的不够'最近(C#9),他们还介绍了一个表达式的语言:

右侧的块极为有限;它是一组唯一的职业分配器。 (C#也最近引入了" init-only"属性(有效地,命名的构造函数参数),因此上面导致新的点不存在,其中在块中写入的属性分配覆盖左操作数的属性分配。)

C#方法对它们是明智的,因为它们可以在它们上构建它们(默认参数,属性),但只是复制该方法会用很多行李拖累。但我们已经拥有了,在Java中,我们需要(几乎)以不同的方式,可能更加丰富:构造函数和解构器。

重建表达式采用其静态类型为T和A块的操作数,其中块对操作数的状态表达了功能转换,并产生了T类型的新实例:

在这里,PP将有任何Y值p,x = 3。该块可以为Java语句(分配,循环,方法调用等)的任意序列进行一些限制。理想情况下,我们可以定义若丙族类的重建,而不仅仅是记录,而是我们' ll从记录开始。招商局具有规范构造函数和解构模式。含义可以将上面的表达方式解释为:

声明一个新的块,新的可变当地人,其名称和类型的记录组件呈计;

用规范解构器解构目标,并将结果分配给上面描述的变量;

在该范围内执行块,可以将这些LOCALSIF突变使用正确的名称;

{//新的范围点(var x,var y)= p; //用规范CTOR解析LHS {x = 3; } //执行该范围的RHS产生新点(x,y); //重建新值}

我们可以在表达式上思考RHS上的块作为记录状态的功能动态形态。因此,它可以合理地对其施加限制。出于稍后将清楚的原因,我们将写入写入除了对应于外提示组件的当地人之外的任何变量,以及块内声明的当地人。该块是FreeTo使用语言(如循环和条件)的任何功能,而不是justAsigmer - 它不是A" DSL"它' SA块的Java代码,它表达了状态的Atransformation记录。

客户端可以与表达式一起使用,但类也可能还希望使用它们的Intheir实现。例如:

记录复杂(双重真实,双IM){复合共轭(){用{Im = -im返回它; }}}复杂realonly(){用{im = 0返回它;复杂的imonly(){用{re = 0返回它; }}}

客户当然可以执行这些相同的操作,但作者可以在这个域中进行操作令人遗憾的是,它使其感到意识到是一种方法。

请注意,如果规范构造函数检查不变量,那么一个带表达也会检查它们。例如:

记录Rational(int num,int denom){Rational {if(enom == 0)投掷新的IllegalArgumentException("否定不能为零"); }}

我们将获得同样的例外,因为这将是将数字解压缩分母解压缩到可变的当地人,使分母变为零,然后将它们返回到规范构造函数 - 谁将抛出。

我们很少的我们必须要开始支持表达式记录;所有构建块都已到位。但在我们去那里之前,韦弗坦要确保我们有一个路径将其扩展到任何想要在本协议中的追容性的类。

什么使得与记录一起使用的事情是我们有一个规范构造函数anddeconstruction模式,他被称为稳定的签名,名称,以及彼此匹配。我们可以将其解释为外部陈述,我们知道如何映射到内部状态描述(在记录的情况下与外部描述相同,可能包含一些验证。)普通类不会有所有这些,因为它们可能存在不同的表示,但是可以添加足够的足够数据,以导出这些行为。

功能转换取决于解构,因此我们需要一种方法向任意类进行DeconStructor,这是在计划上。出于本文件的目的,我们将表示解构模式,如:

但是,我们'还没有那里。一个类可能有多个构造函数anddeconstructors;我们必须选择一个匹配的对,然后提取所有相关的待命序列,然后愿意将修改状态重新包到一个新对象中。 WENED一种选择正确的构造函数和解构器的方法。

显然,选择过载的传统方式赢得了'在这里工作,但是我们可以通过参数名称来实现另一种方式。假设参数名称是重要的(尽管我们知道他们不是。)我们可以解释

作为过载选择问题:"找到一个(最大)upling函数,它需要x。然后,将参数列表中的所有名称和类型用于该编曲器,并找到我的一个(最小)解构器,提取它们所有。"但是我们甚至找到初始集{x}?这是我们限制(无论如何是合理的)进来的地方;我们可以查看块中的分配标准,这些块不会分配给块中声明的当地人,它们必须分配给"属性"被重建的对象。

在此功能需要进行构造函数和解构器的参数和绑定名称(以及事实证明,此类其他可爱的功能也是如此。)我们肯定可以' t只是说"从今天开始,他们重要的"但我们可以让人们选择一个重点到重要名字。在本文档中的博览会上,我们'在构造函数或解构器上LLUSUS Modifier __byName,以指示该名称很大。

自从我们在客户站点上使用这些名称进行预测以来,我们还需要Todefine如何重载__byname构造函数和解压缩器,以及如何进行过载选择。作为一个草兵,我们' ll拍摄最简单的东西,只能工作:只有一个__byname构造函数或解构器。后来我们'据揭示这一点。如果只有一个__byname构造函数anddeconstructor,则清楚地给出:

用__byname解构器解构目标,在类中找到的__byname解构器是目标的静态类型;

在此上下文中执行RHS块,其中限制它可以只执行其名称在构造函数中显示的变量;

以这种方式,任何类都有一个__byname构造函数和解压缩函数在重建协议中的canparticipate。

"只有一个构造函数和deconstructor"规则过于限制,索勒特' s延伸到更有用。关于根据其参数类型和位置的超载和选择成员的现有规则不需要更改,但我们需要新的规则来重载__byname成员(彼此的BothageSt,以及针对位置声明)。

每个__byname构造函数/解压缩程序可以在位置调用,因此它遵循现有规则以进行重载和过载选择。一组__byname成员有效,如果才有才能且仅当每个这样的成员时,它们的名称集要么脱节,也只有一个是另一个是另一个是一个适当的子集,并且对应于公共名称的类型是相应的。这确实是将__byname成员组织成不相交的伸缩链。

示例,这意味着我们选择具有X的最大(链条)构造函数的最大(顶部成员),然后找到最小的Deconstructor在该选定的构造函数中包含所有名称。这再现了最大保真度和最小提取成本的对象。

重载规则起初可能会限制,但它们并不像糟糕的isthey似乎。许多当前的构造函数过载 - 特别是对于功能转换的压缩 - 是"伸缩"品种,其中超载形成X-is-shorthan-for-y的线性链,并且存在更简单的仅作为位置便利,提供默认值(新的Hashmap()代表到新的HashMap(initCapacity),该代表委托给新的HashMap(initCapacity,LoadFactor)。)

部分选择了该方案,因为某些类可以具有与其外部API的全面内部表示,而是希望使用外部API来支持客户端的功能和实现的InternalEupration。通过具有用于内部表示的内部表示的私有构造函数/解压缩程序对,ANDA在外部API方面用于客户端使用的公共对,两个客户端和Adimlementation都可以利用对它们的感觉术语来利用功能转换。并且在公共和普遍的对之间存在重叠的范围,私有对中的名称可以始终是alpha-命名的。

__byname修饰符是诸如许多其他有用程序的底层的一部分,例如基于关键字的调用(新的foo(x:3)),带有或没有参数,基于关键字的解构和普通类的自动生成readcare,如普通类(您可以将一个解构器作为淘压者,并从中获取吸气器。)我们'请谈论这些时间。

功能转换取决于具有__byname构造函数,但是许多发光者更喜欢公开工厂而不是构造函数。我们可以指定__byname工厂来取代这个角色吗?

第一个问题是出厂方法是API设计模式,而不是泛al功能。我们可以用一些简单的糖来解决这个问题;我们caninerpret:

这是一项琐碎的转型,作为奖金,使我们能够在&#34中包含契约方法;构造函数" javadoc的部分。然后我们可以允许__byname工厂,并扩展过载规则,以将所有__byname构造函数和工厂视为组。为了支持FunctionalTransformation,我们可以扩展规则以允许选择__bynameConstructor或工厂。

奖励圆:接口?我们已经允许界面中的静态工厂,并且没有理由'否则接口不能具有解构器的原因(如果接口界面提供了允许提取状态组件的API)。如果我们还允许Deconstructors在接口中,我们甚至可以Dowith'在接口上也是:

接口尖尖{public __byname factory(int x,int y){return new pointimpl(x,y); public __byname __deconstructor尖头(int x,int y){__match(x(),y()); public int x(); public int y();} Pointy P = Pointy.of(1,2);尖头翻转= p,{x = -x; y = -y; };