Предотвращение вызова метода с использованием трюков компилятора - PullRequest
3 голосов
/ 25 мая 2011

У меня примерно такие типы:

interface Record {}
interface UpdatableRecord extends Record {}

interface Insert<R extends Record> {

    // Calling this method only makes sense if <R extends UpdatableRecord>
    void onDuplicateKeyUpdate();
}

Я хотел бы иметь дополнительное ограничение на <R> при вызове onDuplicateKeyUpdate().Причина этого в том, что этот метод имеет смысл, только когда <R> связан с любым подтипом UpdatableRecord, а не только с Record.Примеры:

Insert<?>               i1;
Insert<Record>          i2;
Insert<UpdatableRecord> i3;

// these shouldn't compile
i1.onDuplicateKeyUpdate();
i2.onDuplicateKeyUpdate();

// this should compile
i3.onDuplicateKeyUpdate();

Можно ли использовать какой-либо прием или способ добавить дополнительное ограничение для универсального типа класса только для одного объявления метода?

NOTE :

  • Объявление Insert<R extends UpdatableRecord> не вариант, потому что мне нужна гибкость, позволяющая иметь Insert<R extends Record> для записей, которые не могут быть обновлены
  • Производить UpdatableInsert<R extends UpdatableRecord> extends Insert<R> и выдвигатьМетод там может быть вариант, но я действительно не хочу вводить новый тип.У меня есть несколько из этих методов с несколькими различными ограничениями, которые могут привести к взрыву типа.
  • Бросок UnsupportedOperationException вполне очевиден, но, похоже, способ сделать это в Java 1.4.Мне действительно интересно, могу ли я использовать дженерики и компилятор для этой проблемы.

Идеальное решение :

// This would be an annotation to be interpreted by the compiler. There is no such
// thing in Java, as far as I know. But maybe there's a trick having the same effect?
@Require(R extends UpdatableRecord)
void onDuplicateKeyUpdate();

Ответы [ 2 ]

4 голосов
/ 25 мая 2011

Если вы определяете его в интерфейсе, то он должен быть реализован в любом классе, который хочет реализовать интерфейс. Таким образом, вы не сможете обойти реализацию onDuplicateKeyUpdate в любом классе, который реализует интерфейс Insert<R extends Record>.

Вы указали R extends Record в интерфейсе, и это становится частью контракта, что означает, что можно использовать любой подтип Record. Я не могу придумать способ добавить дополнительное ограничение с использованием обобщений, которые ограничивают его только типом UpdatableRecord.

Кроме того, вы не можете принимать решения в своем интерфейсе на основе типа R, поскольку эта информация исчезает из-за стирания типа.

Мне кажется, что ваша проблема связана с разницей между Record и UpdatableRecord. Поскольку интерфейс предназначен для генерального контракта, я не думаю, что специфичное для типа поведение должно быть в вашем интерфейсе. Таким образом, вы должны найти способ разрешить разницу другим способом. Моим решением было бы реализовать метод (который возвращает boolean) в интерфейсе Record с именем canUpdate, который можно использовать в onDuplicateKeyUpdate. Таким образом, вы можете выбросить UnsupportedOperationException, если запись не поддерживает эту операцию или ничего не делать (если вы хотите избежать исключения и если ничего не делать, имеет смысл в контексте вашей бизнес-логики).

Это с моей головы; может быть лучшее решение. Я постараюсь еще об этом подумать.

EDIT

Я немного подумал об этом, и именно твоя последняя редакция заставила меня задуматься над этим. <R extends Record> относится ко всему классу. Поэтому нет способа переопределить или сузить его в контексте определенного метода. Аннотация, подобная той, которую вы упомянули, должна быть реализована на уровне компилятора, и на этом уровне я даже не уверен, будет ли у вас доступ к типу R (из-за стирания типа). Так что то, что вы говорите, может быть невозможным.

На концептуальном уровне то, что вы просите , не должно быть возможным IMO, потому что вы указываете исключение из контракта в пределах вашего контракта. Также кажется, что детали реализации проникают в ваш интерфейс, потому что интерфейсы не должны пытаться выяснить, что что-то может или не может делать; это должно быть решено в конкретной реализации.

1 голос
/ 26 мая 2011

Вивин хорошо отмечает контракт, который вы заключили, когда создали Insert.Я хотел бы спросить, действительно ли onDuplicateKeyUpdate() имеет смысл в этом контексте.Это кажется довольно специфичным только для одного подтипа Record.

. Если вы можете свернуть эту функциональность в другой метод Insert (скажем, execute()?), Вы можете проверить тип объекта и действоватьсоответственно.Например,

[pseudo-code]
method execute( R record )
 try
  insertRecord( record )
 catch ( KeyAlreadyExistsException e )
  if ( record instanceof UpdatableRecord )
   updateRecord( record )
  end if
 end try
end method

Если вы ищете более объектно-ориентированный подход, вы можете вставить логику для вставки в сами подклассы Record.Там я бы изменил Insert на Insertable, присвоил бы ему метод insert(), и Record внедрил бы Insertable.Тогда каждый подкласс Record может знать правильный путь к insert() сам.Хотя я не уверен, соответствует ли это вашей стратегии дизайна ...

...