如何扩展Java API以便能够引入新的注释



你能解释一下我如何扩展或更改JAVA-API以使用两个新的注解@any@option来允许在JAVA中使用乘法吗?

多重性的主要思想如下:

当我们将多对多关系更改为一对一关系时,多重性有助于解决许多维护问题,反之亦然。

我想对"字段"、"参数方法"one_answers"返回参数"使用上面的注释。

例如:

class MyClass {
String @any name; // instead of using List<String>, i would like to use @any 
public String setFullname(String @option name) { // @option is another multiplicity
...
} 
}

为了允许这个定义,我必须更改JAVA-API并用这两个注释对其进行扩展,但我不知道如何做到这一点。

你能告诉我如何改变API,以及我必须遵循哪些步骤才能达到我的要求吗?


请查看本文以了解问题。

正如在那篇论文中所解释的,使用多重性来建立多个关系会导致许多问题:

  1. "它使维护变得乏味且容易出错。"<lt;如果程序的维护需要将关系从一个变为多个(反之亦然),那么程序中几乎每一个代表这种关系的变量都需要被触及。在具有静态类型检查的语言中,出现以下情况将在字段声明更改后由编译器识别(作为类型错误),这样至少不会忘记使用;在没有它的语言中,更改极易出错>>

  2. "它改变了分型的条件"<lt;如果S是T的一个子类型,则可以将类型为S的表达式分配给类型为T的变量(表示一对一关系)。当关系升级为多,并且表达式和变量的类型更改为Collection和Collection以反映这一点时,赋值就不再是好类型[18]。要解决此问题,必须将变量(从前为一,现在为tomany)的使用限制为检索其集合的元素,这可能需要对代码进行实质性的进一步更改。我们认为对多重性进行分型会很尴尬。>>

  3. "它改变了调用语义"不连续性的另一个表现形式是,当一个包含相关对象的变量被用作方法的实际参数时使用按值调用语义进行调用时,该方法无法更改变量的值(即变量指向哪个对象),因此无法更改哪个对象变量的所有者与相关。相比之下,当变量持有相关对象的集合时,通过将该变量按值传递给方法可以方法来添加和删除集合,从而更改变量所有者与哪些对象相关,从而有效地提供了通过引用调用的语义。我们认为这种语义对多重性的依赖是很尴尬的。当然,有一个简单的解决方案可以解决由所指出的不连续性引起的所有问题:还使用容器实现一对一关系。例如,Scala中的Option类有两个子类,Some和None,其中Some包装类型为E带有一个Option类型的对象,该对象可以用None代替,这样该对象和任何对象都有一个统一的访问协议(即Option的访问协议)。通过选择执行托收协议,上述不连续性将大部分消失。然而,这样做概括了收藏的问题,这些问题源于将内容放在容器上。具体而言:

  4. "相关对象必须先展开才能使用"。使用容器来保存相关对象,对表示关系的变量可执行的操作是容器的操作,而不是相关对象的操作。例如,如果cookie具有beNibbled()操作,则相同的操作可以通常不应来自Cookie集合(因为集合是一个常规目的类别)。

  5. "它将可替代性置于泛型的分型规则之下"。虽然一到多变量(上文第2项)在分型上的差异删除后,错误的版本仍然存在:现在,与目标类型T的一对一关系,实现为具有Option类型的字段,无法与T的子类型s的对象相关(使用Option,除非接受有关替换对象的限制)。

  6. "它引入了一个混叠问题"。虽然别名是面向对象编程的一个常见问题(例如,参见[11,19]),但使用容器对象来实现关系引入了别名容器的特殊问题:当两个对象共享同一容器时,一个对象的关系不能与另一个对象不同。然而,这可能无法正确地对域进行建模,并可能导致细微的编程错误。

  7. "表现贫困"。更普遍地说,只考虑集合是不可能的表示"对象a有一个集合,其中包含对象b1到bn"one_answers"对象a具有对象b1到bn"之间的区别。虽然人们可能会认为前者只是表示后者的面向对象方式,所使用的集合只是一个实现对象,但也可能是集合实际上是一个域对象(因为它甚至可以有别名;参见上文)。相反,在面向对象建模中,充当实现类的集合是通过指定大于1的倍数(可能由集合类型的约束来补充,即集合是否有序、是否重复等)来抽象的。因此,域模型中的集合类始终是域类。

下图使用互联网服务提供商域中的示例程序突出显示了这些问题。

http://infochrist.net/coumcoum/Multiplicities.png

最初,客户可以拥有一个电子邮件帐户,根据所选的价格计划,该帐户可以是POP3帐户,也可以是IMAP帐户。帐户由工厂创建(静态方法Account.make,图1左侧,从第4行开始),由于对称性的原因,也由静态方法删除(Account.delete;第19行);然而,由于Java不支持通过引用(或out参数)调用,delete无法按预期工作。因此,在方法Customer.transferAccount(第40行)中复制了将字段帐户重置为null的操作。当程序升级为支持每个客户多个帐户时,第一个更改是将帐户类型更改为List(图1右侧第30行)。正如上述问题1所建议的那样,这需要进行许多更改。在Customer类中,它要求在所有帐户上引入迭代(第35行),并用迭代变量a替换方法集account的接收者(问题4)。在Account类中,必须更改make以返回帐户列表(第4行),并且帐户的构造(第7行和第12行)必须替换为包含适当类型的单个帐户的列表的构造。诚然,让账户工厂退货似乎很尴尬;然而,正如我们将看到的,它只演示了问题7。此外,它还带来了子类型条件的变化(问题2):为了使make类型良好(图1右侧没有),它的返回类型必须更改为List(需要相应更改Customer.account的类型,从而限制account用于读取访问;问题5),或者创建的列表需要更改为元素类型account。Account.delete的参数类型也需要更改为List;替换分配清除列表(第20行)以更好地反映帐户的缺失(参见上面关于null的不同含义的讨论)使删除按预期工作,但这可能会改变实际调用delete的程序的语义(问题3)。在类Account的第40行中,从分配null到调用clear()的类似更改引入了一个逻辑错误,因为转移的帐户也被意外清除了(问题6)。

解决方案是使用多重性,如下所示(查看下面的图片评论):

现在的问题是,如何在Java中实现多重性?

您对API的含义感到困惑。要实现这个想法,您需要编辑Java编译器的源代码,最终得到的将不再是Java,而是Java的分叉版本,您必须调用其他东西。

老实说,我认为这个想法没有多大价值。

不清楚为什么您认为这可以解决您的问题,而使用非标准JDK实际上会给您带来更大的维护负担。例如,当JDK有新版本时,升级时也需要将更新应用到新版本。更不用说你雇佣的新员工不会熟悉你偏离Java的语言。

Java确实允许定义自定义注释:http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html

并且可以使用反射或注释处理器来用它们做一些很酷的事情但是如果不派生您自己版本的JDK,就不能使用注释来如此彻底地更改程序语义(比如神奇地使String表示字符串列表),这是个坏主意。

最新更新