在Java接口中重新定义wait方法

时间:2011-06-18 20:43:11

标签: java sql object overloading api-design

我想将wait(int)用作流畅API(用于http://www.jooq.org)中方法的签名。目标是能够像这个例子那样构建SQL查询:

SELECT * FROM T_AUTHOR
WHERE ROWNUM <= 1
FOR UPDATE OF FIRST_NAME, LAST_NAME
WAIT 5

完整的FOR UPDATE子句语法规范(至少对于Oracle)可以在这里看到:

FOR UPDATE [ OF [ [ schema. ] { table | view } . ] column
             [, [ [ schema. ] { table | view } . ] column]...]
[ { NOWAIT | WAIT integer | SKIP LOCKED } ]

http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/img_text/for_update_clause.htm

使用jOOQ,我真的希望接近SQL语法。所以我希望能够使用jOOQ流畅的API对上面的SQL子句进行建模,如下所示:

Result<Record> result = create.select()
                              .from(T_AUTHOR)
                              .limit(1)
                              .forUpdate()
                              .of(FIRST_NAME, LAST_NAME)
                              .wait(5) // Here's the issue
                              .fetch();

fetch方法用于将API的底层对象呈现为SQL,并针对Oracle(或任何其他)数据库运行SQL语句。以上内容可以在界面中合法指定:

/**
 * A type that models a "step" in the creation of a query using the fluent API
 */
public interface SelectForUpdateWaitStep extends SelectFinalStep {
    // [...]

    /**
     * Add a "FOR UPDATE .. WAIT n" clause to the query
     */
    SelectFinalStep wait(int seconds);

    // [...]
}

我对此有一些疑问,因为存在与另一种方法发生冲突的风险:

public class Object {
    // [...]

    public final native void wait(long timeout) throws InterruptedException;

    // [...]
}

感谢方法重载(intlong参数),我实际上可以做到这一点。但我担心它可能会让我的用户感到困惑并导致错误。所以这是错误的:

                              .forUpdate()
                              .of(FIRST_NAME, LAST_NAME)
                              .wait((long) 5) // This doesn't make sense
                              .fetch();       // This doesn't compile

所以我的问题是:

  1. 我可以以某种方式阻止呼叫/访问Object.wait(long) altoghether吗?我不这么认为,因为它被声明为final,但也许有人知道编译器技巧或其他什么?
  2. 除了将方法重命名为谦虚的doWait(int)WAIT(int)之外,您对我的API设计有更好的了解吗?

4 个答案:

答案 0 :(得分:4)

您可以尝试使用waitFor方法,它指定要等待的时间和“条件”。实现细节将被隐藏,但一种可能的实现方式是立即尝试您的操作并循环,直到满足指定的条件,并在尝试之间进行适当的暂停。

以下是我自己使用Condition的示例界面(如您所见,它不需要复杂):

public interface Condition {
    public boolean met();
}

答案 1 :(得分:3)

为了DSL而乱用核心Java并不是一个好主意。

为什么不让你的DSL更具表现力?

等待(int n)意味着什么?等待N毫秒,秒,分钟?

更好的签名是:

等待(持续时间长,java.util.concurrent.TimeUnit){...}

读得更好,例如:

等待(30,TimeUnit.MILLISECONDS)

答案 2 :(得分:3)

void wait(long)Object提供的合同的一部分,因此不应更改。想象一下,有人存储您的对象并尝试将其用于wait/notify线程逻辑。所以彻底改变它的逻辑就是违背规则。所以你必须提出不同的名字。

另一方面,似乎有forUpdate参数表示等待时间将适合该法案。除了现有版本之外,您还可以拥有其他版本的forUpdate

答案 3 :(得分:2)

这需要一种禁用Object方法的方法。主要原因似乎是因为它有一个漂亮的名称,适合专有API的目的。

首先,这与继承的整个想法相矛盾 - 一旦从类继承,所有子类必须暴露相同的非私有字段&amp;方法。您始终可以覆盖方法,除非(1)将其标记为final并且(2)它具有不兼容(非协变)返回类型,这两种方法都适用于void wait(long)方法

此外,由于Java中的每个对象都是Object,因此所有内容都必须有方法void wait(long),并且无法隐藏/删除/禁用/转发/覆盖它。假设有可能隐藏void wait(long)方法,如果你想调用它,你将如何调用它?

但是,假设您永远不需要为特定类调用void wait(long),那么始终采用 AspectJ 使用的源/字节码编织方法来进行更改基于某些调用规则的.class Java字节码。您可以捕获每次调用wait(long)并声明错误/警告。点击此处:http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations-decp.html

但是,即使使用带字节码编织的AspectJ,也无法使用本机方法切入点。最有可能的是,即使使用源代码编织,这也是不可能的 - 但它可能值得一试。