Ввод метода клонирования, указанного в интерфейсе

Я пишу интерфейс, который требует, чтобы классы реализовывали метод 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, т. е. интерфейс указывает метод для клонирования в тип интерфейса, а реализующие классы должны дополнительно иметь этот метод. к любому более конкретно типизированному методу клонирования, который у них может быть. Этот вариант мне показался наименее неприятным.


person fenomas    schedule 30.08.2009    source источник


Ответы (3)


AS3 не позволяет вам выполнять перегрузки (более одной функции с одним и тем же именем, различающиеся типами возвращаемого значения или типами параметров), а только переопределения (подклассы могут заменить реализации базового класса).

e.g.

function foo():int {}
function foo():String {}
function foo(a:String):void {}

Являются перегрузками функции с именем foo — вы не можете сделать это в ActionScript. Интерфейсы действительно ближе к переопределениям, чем к перегрузкам, в том смысле, что вы в некотором смысле «наследуете» интерфейс.

// in your specific case you couldn't have these two functions
// defined within the same scope
public function clone():MyGame {}
public function clone():ISolvableGame {}

То, что вы пытаетесь сделать, это смешать две концепции. Вы хотите перегрузить интерфейс, который пытаетесь переопределить. Как показывает пост Фредрика, даже в языках, поддерживающих перегрузку (AS3 не входит в их число), вы часто не можете делать и то, и другое одновременно. Интерфейс заставляет вас иметь функцию с точной сигнатурой: clone():ISolvableGame и вы не можете перегрузить ее дополнительным clone():MyGame

Если вы ищете безопасность типов, вы не можете получить ее во время компиляции, но вы сможете получить ее во время выполнения, если вы проверите тип возвращаемого значения в вызывающем объекте.

e.g.

// this will throw if clone does not return an
// object that is cast-able to MyGame
var game:MyGame = MyGame(someObj.clone());

Я бы тоже предпочел иметь возможность обеспечить соблюдение контракта в MyGame, но я не знаю, как это сделать.

person James Fassett    schedule 30.08.2009
comment
Спасибо за комментарий .. пожалуйста, посмотрите мое обновление и дайте мне знать, если вы согласны с моим резюме. - person fenomas; 01.09.2009

У меня нет опыта работы с ActionScript, поэтому это может быть или не быть рабочим решением, но в C# я обычно делаю так:

public class MyGame : ISolvableGame {
    public MyGame clone(){
        // ...
    }

    ISolvableGame ISolvableGame.clone() {
        return this.clone();
    }
}

Другими словами, я делаю "типизированный" метод клонирования (который не реализует интерфейс, так как он отличается по возвращаемому типу). Затем я делаю явную реализацию метода интерфейса, который будет вызывать типизированный метод клонирования и возвращать результат. Это допустимый ход, так как MyGame можно превратить в ISolvableGame.

person Fredrik Mörk    schedule 30.08.2009
comment
Что ж, я определенно могу сделать это, если два метода имеют разные имена, но, к сожалению, AS3 не допускает такого рода перегрузку... поэтому любой вызывающий должен знать, следует ли вызывать clone() или cloneToISolvable(), что в некотором роде неприятно. Хотя может это единственный выход.. - person fenomas; 30.08.2009

Унаследованные методы клонирования всегда имеют суперкласс в качестве возвращаемого типа. Например, все классы событий переопределяют метод клонирования класса flash.events.Event, который возвращает объект Event, и реализуют его для возврата соответствующего объекта производного класса.

person Amarghosh    schedule 01.09.2009