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