如何扩展Java API以引入新注释

时间:2013-12-02 21:10:39

标签: java annotations java-api

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

Multiplicities的主要思想如下:

  

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

我想将上述注释用于"字段","参数方法"和"返回参数"。

例如:

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,以及我必须遵循哪些步骤来实现我的要求?


请查看this paper以了解问题。

正如那篇论文所解释的那样,使用多重性构建多对多关系会导致许多问题:

  1. &#34;它使维护繁琐且容易出错。&#34; &LT;&LT;如果程序的维护需要将关系从一对多更改为多对(或反之亦然),则几乎每次出现在程序中表示该关系的变量都需要被触及。在具有静态类型检查的语言中,这些事件发生 在声明字段的声明被更改之后,编译器将识别(作为类型错误),这样至少不会忘记使用;在没有它的语言中,这种变化极易出错&gt;&gt;

  2. &#34;它改变了子类型的条件&#34; &LT;&LT;如果S是T的子类型,则可以将类型S的表达式赋值给类型T的变量(表示一对一关系)。当关系升级为to-many并且表达式和变量的类型被更改时收集和收集以反映这一点,分配不再是良好的类型[18]。要解决这个问题,必须限制使用(以前的to-one和now tomany)变量来检索其集合的元素,这可能需要进行大量的进一步代码更改。我们考虑这种依赖性 对多重性进行子类型化是尴尬的。&gt;&gt;

  3. &#34;它改变了呼叫语义&#34; 不连续性的另一个表现是当保持相关对象的变量被用作方法的实际参数时 用call-by-value语义调用,该方法不能改变变量的值(即变量指向哪个对象),因此不能改变哪个对象 变量的所有者与之相关。相反,当变量包含相关对象的集合时,将此变量按值传递给方法允许 从集合中添加和删除的方法,从而改变变量所有者与之相关的对象,从而有效地给出调用by-reference语义。 我们认为语义对多重性的这种依赖性是尴尬的。当然,可以轻松解决因所发现的不连续性而产生的所有问题: 使用容器实现一对一的关系。例如,Scala中的Option类有两个子类,Some和None,其中Some包装了一个类型的对象 E具有Option类型的对象,可以用None替换,以便对象和没有对象具有统一的访问协议(即Option的协议)。通过制作选项 实施收集协议,上述不连续性将大部分消失。然而,这样做概括了源于此的集合问题 将内容放在容器上。具体做法是:

  4. &#34;相关对象必须先解开才能使用&#34;。 使用容器来保存相关对象,在表示关系的变量上可执行的操作是容器的操作而不是相关对象的操作。例如,如果cookie具有beNibbled()操作,则相同的操作可以 通常不会从一组cookie中得到预期(因为Collection是一般的 目的类)。

  5. &#34;它可以替代仿制药的子类型规则&#34;。虽然在一对一和多对多变量(上面的第2项)之间的子类型的差异已经存在 删除,错误的版本幸存下来:现在,与目标类型T的一对一关系,实现为具有类型Option的字段,不能与T的子类型S的对象相关(使用Option,除非接受关于替换对象的限制)。

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

  7. &#34;富有表现力的贫困&#34;。 更一般地说,仅给出集合是不可能的 表达“对象a有一个集合,其中包含对象b1到bn”和“对象a有对象b1到bn”之间的区别。虽然可以认为前者仅仅是面向对象的方式来表示后者,并且使用的集合仅仅是一个实现对象,但可能是集合实际上是一个域对象(它甚至可能具有它)别名;参见上文)。相反,在面向对象的建模中,通过指定大于1的多重性来抽象用作实现类的集合(可能通过对集合类型的约束来补充,即,它是否被排序,具有重复等)。因此,域模型中的集合类始终是域类。

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

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

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

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

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

2 个答案:

答案 0 :(得分:3)

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

诚实地说,我不认为这个想法有很多优点。

答案 1 :(得分:1)

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

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

...并且可以使用反射或注释处理器来完成它们的酷事。 然而,注释不能用于如此彻底地改变程序语义(比如神奇地使字符串意味着字符串列表,而不是分支你自己的JDK版本,这是一个坏主意。) / p>