Я пишу интерфейс, который требует классов для реализации метода clone (). Мой наивный подход к этому был следующим:
public interface ISolvableGame {
function clone():ISolvableGame;
//...
}
в другом месте:
public class MyGame implements ISolvableGame {
public function clone():MyGame {
// ...
}
}
Я предполагал, что подпись такого рода будет законной, потому что MyGame.clone()
возвращает экземпляр класса, который реализует ISolvableGame, который, как мне кажется, удовлетворяет контракту в интерфейсе. Однако код, подобный приведенному выше, генерирует ошибку компиляции, ссылаясь на тот факт, что MyGame.clone()
имеет сигнатуру, отличную от той, которая указана в интерфейсе.
Таким образом, мой вопрос: как мне создать интерфейс, требующий клонирования, если реализованный метод должен точно соответствовать сигнатуре в интерфейсе? Очевидно, что делать интерфейс более конкретным не имеет никакого смысла. Но если бы я сделал реализованный метод менее конкретным (т. Е. Если бы я набрал MyGame.clone()
как возвращающий ISolvableGame
), другие пользователи этого метода клона больше не будут знать, что они получают.
Нужны ли мне две версии метода clone, одна из которых напечатана как ISolvableGame
для соответствия интерфейсу, а другая - как MyGame
для использования в классе? Или есть лучший подход?
Примечание: Я работаю в ActionScript3 (Java-подобный язык, который реализует спецификацию ECMA4). Я отметил это как независимый от языка в предположении, что AS3 не уникален в том, как он обрабатывает интерфейсы. Но если приведенный выше пример кода будет работать на других языках, моя проблема может быть связана с моим языком.
Обновление: Мне пришло в голову выяснить, как с этим справляются основные библиотеки моего языка. Например, существует интерфейс IEventDispatcher
, который определяет метод dispatch():Event
- так что любой класс, отправляющий подкласс из Event
, не может реализовать IEventDispatcher
, что в конечном итоге похоже на мою проблему.
Основные библиотеки справляются с этим, вместо этого имея такие классы , наследующие от класса EventDispatcher
, который существует с целью реализации IEventDispatcher
. Таким образом, достигается безопасность типов во время компиляции, но за счет того, что в первую очередь необходимо уменьшить смысл использования интерфейса, поскольку обычно предпочитают интерфейсы, чтобы избежать проблем, связанных с наследованием.
Я думаю, что мой выбор:
- В конечном счете, полагайтесь на наследование, как это делают основные библиотеки
- Реализовать два метода, как описывает Фредерик, с разными именами
- Пожертвуйте безопасность во время компиляции, как описывает Джеймс
Ответ: В конце концов, я остановился на опции, позволяющей интерфейсу указывать метод cloneToSolvable
- то есть интерфейс определяет метод для клонирования типа интерфейса, и классы реализации должны иметь этот метод в дополнение к любому более конкретно типизированному методу клонирования, который у них может быть. Это показалось мне наименее неприятным из вариантов.