Re-define wait method in a Java interface
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)
开发者_JS百科 .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)
?
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):
public interface Condition {
public boolean met();
}
void wait(long)
is a part of the contract offered by Object
and therefore it should not be changed. Imagine that someone stores your object and attempts to use it for wait/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 of forUpdate
in addition to existing one.
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 the void wait(long)
method.
Furthermore, since every object is an Object
in Java, everything must have a method void wait(long)
and there should be no way to hide/delete/disable/forward/override it. Assuming it were possible to hide the void 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 to wait(long)
and declare an error/warning. See more here: http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations-decp.html
However, 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.
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)
精彩评论