在Java接口中重新定义等待方法
我想使用 wait(int)
作为 Fluent 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 } ]
完整 oracle.com/docs/cd/B28359_01/server.111/b28286/img_text/for_update_clause.htm" rel="nofollow">http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/img_text/for_update_clause.htm
使用jOOQ,我真的很想接近SQL语法。因此,我希望能够使用 jOOQ Fluent 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;
// [...]
}
感谢方法重载(int
vs. long
参数),我实际上可以做到这一点。但我担心这可能会让我的用户感到困惑并导致错误。所以这是错误的:
.forUpdate()
.of(FIRST_NAME, LAST_NAME)
.wait((long) 5) // This doesn't make sense
.fetch(); // This doesn't compile
所以我的问题是:
- 我可以以某种方式阻止调用/访问
Object.wait(long)
吗?我不这么认为,因为它被声明为最终的,但也许有人知道编译器技巧,或者其他什么? - 除了将方法重命名为像
doWait(int)
或WAIT(int)
这样愚蠢的名称之外,您对我的 API 设计还有更好的想法吗?
I would like to use wait(int)
as the signature of a method in a fluent API (used for http://www.jooq.org). The goal is to be able to construct SQL queries like this example:
SELECT * FROM T_AUTHOR
WHERE ROWNUM <= 1
FOR UPDATE OF FIRST_NAME, LAST_NAME
WAIT 5
The full FOR UPDATE
clause syntax specification (at least for Oracle) can be seen here:
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
With jOOQ, I really want to stay close to the SQL syntax. So I'd like to be able to model the above SQL clause with the jOOQ fluent API like this:
Result<Record> result = create.select()
.from(T_AUTHOR)
.limit(1)
.forUpdate()
.of(FIRST_NAME, LAST_NAME)
.wait(5) // Here's the issue
.fetch();
The fetch method is used to render the API's underlying object as SQL and run the SQL statement against an Oracle (or any other) database. The above can be legally specified in an interface:
/**
* 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);
// [...]
}
I have some doubts about this, though, because there is a risk of collision with another method:
public class Object {
// [...]
public final native void wait(long timeout) throws InterruptedException;
// [...]
}
Thanks to method-overloading (int
vs. long
arguments), I can actually do this. But I'm afraid it might confuse my users and lead to mistakes. So this would be wrong:
.forUpdate()
.of(FIRST_NAME, LAST_NAME)
.wait((long) 5) // This doesn't make sense
.fetch(); // This doesn't compile
So my questions are:
- Can I somehow prevent calling/accessing
Object.wait(long)
altoghether? I don't think so because it's declaredfinal
but maybe someone knows a compiler-trick, or something else? - Do you have a better idea for my API design apart from just renaming the method to something silly like
doWait(int)
orWAIT(int)
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您可以尝试改用
waitFor
方法,该方法指定要等待的时间和“条件”。实现细节将被隐藏,但一种可能的实现是立即尝试您的操作并循环,直到满足指定的条件,并在尝试之间适当暂停。这是我自己使用的
Condition
的示例接口(如您所见,它不需要很复杂):You might try using a
waitFor
method instead, which specifies both a time and a "condition" to wait for. The implementation detail would be hidden, but one possible implementation would be to try your action immediately and loop until the specified condition has been met, with an appropriate pause between attempts.Here's a sample interface for a
Condition
I use myself (as you can see, it doesn't need to be complex):void wait(long)
是Object
提供的合约的一部分,因此不应更改。想象一下,有人存储您的对象并尝试将其用于等待/通知线程逻辑。所以完全改变它的逻辑就是违反规则。所以你必须想出不同的名字。另一方面,似乎让
forUpdate
接受指示等待时间的参数将符合要求。除了现有版本之外,您还可以拥有另一个版本的forUpdate
。void wait(long)
is a part of the contract offered byObject
and therefore it should not be changed. Imagine that someone stores your object and attempts to use it forwait/notify
threading logic. So completely changing it's logic is just playing against the rules. So you will have to come up with different name.On the other hand, it seems that having
forUpdate
take parameter indicating wait time will fit the bill. You could just have another version offorUpdate
in addition to existing one.这需要一种禁用
Object
方法的方法。主要原因似乎是因为它有一个好名称,适合专有 API 的用途。首先,这与继承的整个概念相矛盾——一旦从一个类继承,所有子类都必须公开相同的非私有字段和私有字段。方法。您始终可以重写方法,除非 (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,本机方法切入点也是不可能的。即使使用源代码编织,这很可能也是不可能的——但可能值得一试。
What this requires is a way to disable an
Object
method. And main reason seems to be because it has a nice name that would fit the purposes of a proprietary API.At first, this contradicts the entire idea of inheritance -- once you inherit from a class, all subclasses must expose the same non-private fields & method. You can always override a method, except when (1) it is marked as
final
and (2) it has an incompatible (non-covariant) return type, both of which are true with thevoid wait(long)
method.Furthermore, since every object is an
Object
in Java, everything must have a methodvoid wait(long)
and there should be no way to hide/delete/disable/forward/override it. Assuming it were possible to hide thevoid wait(long)
method, how would you go about invoking it, should you wish to invoke it?However, assuming you would never need to invoke
void wait(long)
for your particular classes, there is always the approach of source/byte-code weaving that AspectJ uses in order to make changes to the .class Java bytecode based on certain invocation rules. You could trap every call towait(long)
and declare an error/warning. See more here: http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations-decp.htmlHowever, native method pointcuts are not possible even with AspectJ with byte-code weaving. Most likely, this is not possible even with source-code weaving -- but it might be worth a try.
为了 DSL 而使用核心 Java 根本不是一个好主意。
为什么不让你的 DSL 更具表现力呢?
wait(int n) 到底是什么意思?等待N毫秒、秒、分钟?
更好的签名是:
wait(long period, java.util.concurrent.TimeUnit){ ... }
读起来更好,例如:
wait(30, TimeUnit.MILLISECONDS)
Hacking around with core Java for the sake of DSL is simply not a good idea.
Why not make your DSL more expressive?
What does wait(int n) mean anyway? wait for N milliseconds, seconds, minutes?
A better signature would be:
wait(long duration, java.util.concurrent.TimeUnit){ ... }
which reads better, for example:
wait(30, TimeUnit.MILLISECONDS)