Переопределите метод ожидания в интерфейсе Java - PullRequest
6 голосов
/ 19 июня 2011

Я хотел бы использовать 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. Поэтому я хотел бы иметь возможность смоделировать вышеупомянутое предложение SQL с помощью свободно распространяемого API jOOQ следующим образом:

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 и выполнения оператора SQL для базы данных Oracle (или любой другой). Выше может быть юридически указано в интерфейсе:

/**
 * 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 против long аргументов) я действительно могу это сделать. Но я боюсь, что это может запутать моих пользователей и привести к ошибкам. Так что это было бы неправильно:

                              .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. У вас есть лучшая идея для разработки моего API, кроме простого переименования метода в нечто глупое, например doWait(int) или WAIT(int)?

Ответы [ 4 ]

4 голосов
/ 19 июня 2011

Вместо этого вы можете попробовать использовать метод waitFor, который указывает время и «условие» ожидания.Детали реализации будут скрыты, но одна из возможных реализаций будет заключаться в том, чтобы немедленно выполнить ваше действие и выполнить цикл, пока не будет выполнено указанное условие, с соответствующей паузой между попытками.

Вот пример интерфейса для ConditionЯ использую себя (как вы можете видеть, это не должно быть сложным):

public interface Condition {
    public boolean met();
}
3 голосов
/ 24 июня 2011

void wait(long) является частью контракта, предложенного Object, и поэтому не должен изменяться.Представьте, что кто-то хранит ваш объект и пытается использовать его для wait/notify логики потоков.Так что полностью изменить его логику - просто играть против правил.Поэтому вам придется придумать другое имя.

С другой стороны, кажется, что наличие параметра forUpdate, указывающего время ожидания, будет соответствовать требованиям.Вы могли бы просто иметь другую версию forUpdate в дополнение к существующей.

3 голосов
/ 21 июня 2011

Взломать ядро ​​Java ради DSL просто не очень хорошая идея.

Почему бы не сделать ваш DSL более выразительным?

Что означает ожидание (int n)? ждать N миллисекунд, секунд, минут?

Лучшая подпись будет:

wait (большая продолжительность, java.util.concurrent.TimeUnit) {...}

, который читается лучше, например:

wait (30, TimeUnit.MILLISECONDS)

2 голосов
/ 21 июня 2011

Для этого требуется способ отключения Object метода. И главная причина, по-видимому, заключается в том, что у него есть nice имя, которое соответствует целям проприетарного API.

Во-первых, это противоречит всей идее наследования - как только вы наследуете от класса, все подклассы должны предоставлять одни и те же не приватные поля и метод. Вы всегда можете переопределить метод, за исключением случаев, когда (1) он помечен как final и (2) имеет несовместимый (нековариантный) тип возвращаемого значения, оба из которых имеют значение true для метода void wait(long).

Кроме того, поскольку каждый объект является Object в Java, у всего должен быть метод 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 с переплетением байт-кода. Скорее всего, это невозможно даже при переплетении исходного кода, но, возможно, стоит попробовать.

...