Swift, могу ли я переопределить метод с более конкретным типом производного параметра

Играющая карта наследуется от карты

Учитывая две функции с одинаковым именем:

 func match(othercards : [PlayingCard]) ->  Int {
     return 2 
 }

 func match(othercards : [Card]) ->  Int {
     return 2 
 }

Выдает ошибку, говоря: метод переопределения с селектором 'match:' имеет несовместимый тип '([PlayingCard]) -> Int'

Почему??? Его две функции с одинаковым именем, но с двумя разными типами параметров, почему он все еще запрашивает переопределение? Если я это сделаю, то даже это называется ошибкой.


person sriram hegde    schedule 25.07.2015    source источник
comment
Наследуется ли PlayingCard от Card?   -  person bhspencer    schedule 25.07.2015
comment
Да, наследуется от карты   -  person sriram hegde    schedule 25.07.2015
comment
Тогда в этом проблема   -  person bhspencer    schedule 25.07.2015
comment
Вы пишете другое имя метода, например. matchPlayingCard (другие карты: [PlayingCard]) - ›Int   -  person bhspencer    schedule 25.07.2015
comment
Здесь есть аналогичный вопрос stackoverflow.com/questions/17944322/ это для C #, но принцип тот же для Swift и Java   -  person bhspencer    schedule 25.07.2015
comment
Спасибо! Проголосуйте за мой вопрос. :)   -  person sriram hegde    schedule 25.07.2015
comment
Я пробовал ваш код, но не получаю ожидаемой ошибки (с использованием Xcode 6.4). Вы используете 6.3.x? Так что попробуйте обновить его.   -  person Qbyte    schedule 25.07.2015
comment
Вы можете использовать: если othercards.isKindOfClass (Card) что-то делает, else делает что-то еще.   -  person Swift Rabbit    schedule 25.07.2015
comment
Подождите, я не думаю, что это проблема. Тестировал на Playground + простое приложение. Если карта является классом. А PlayingCard - это класс, расширяющий Card, после чего он компилируется и отлично работает. Протестировано с Xcode 6.4 и Swift 1.2.   -  person Luca Angeletti    schedule 25.07.2015
comment
Хм? Странно, потому что мой xcode - xcode6.4, а swift - 1.2, но я не пробовал это на игровой площадке, я проверил это и дам вам знать, ребята. В любом случае спасибо! : D   -  person sriram hegde    schedule 25.07.2015
comment
@appzYourLife в вашем работающем коде, если вы вызываете match с экземпляром PlayingCard, какой метод вызывается? Как компилятор мог узнать, какой из них вы хотите использовать?   -  person bhspencer    schedule 25.07.2015
comment
@bhspencer: ты прав. Перегрузка выполняется во время компиляции. Итак, здесь компилятор просто смотрит на тип переменной (а не на экземпляр внутри нее). Таким образом, этот код возвращает 1: let card : PlayingCard = PlayingCard() card.match([PlayingCard]()), а этот код возвращает 2: let card : Card = PlayingCard() card.match([PlayingCard]())   -  person Luca Angeletti    schedule 25.07.2015


Ответы (3)


Поскольку PlaingCard наследуется от Card, вам не разрешается переопределять метод таким образом.

Подумайте, что произойдет, если вы попытаетесь вызвать совпадение с экземпляром PlayingCard. Какой из двух методов он вызовет? Это было бы двусмысленно и поэтому не допускается.

В этом случае одним из решений является изменение имени метода, который принимает более конкретный тип. например

 func matchPlayingCard(othercards : [PlayingCard]) ->  Int {
     return 2 
 }
person bhspencer    schedule 25.07.2015
comment
Вы представляете это так, как будто это обязательно двусмысленно, а это не так. Компилятор мог просто выбрать ближайший по иерархии. - person User; 25.07.2015
comment
Я не уверен в быстром, но в Java это называется ковариантным параметром, и это не разрешено. java- tips.org/java-se-tips-100019/24-java-lang/ - person bhspencer; 26.07.2015
comment
Это перегрузка и не имеет ничего общего с ковариацией, по крайней мере, в этом контексте. Ссылка, которую вы публикуете, относится к переопределению метода (или реализации интерфейса). Фактически вы можете перегрузить разные типы одной и той же иерархии в Java. За исключением случаев, когда это параметр типа (например, List<MyType> и List<MySuperType>), но это ограничение возникает из-за стирания типа, которого нет в Swift. - person User; 26.07.2015
comment
@Ixx Согласен. Это перегрузка, и это совершенно законно в Swift. Вам просто нужно избегать расширения NSObject. - person Luca Angeletti; 26.07.2015
comment
Как это может быть перегрузкой, если исходный вопрос говорит о переопределении? Даже сообщение об ошибке, включенное в вопрос OPs, указывает, что происходит переопределение. - person bhspencer; 26.07.2015
comment
Что ж, все мы знаем текущее качество сообщений об ошибках в Swift :) ... возможно, это вызвано внутренним представлением или чем-то еще. Рассматриваемый код явно перегружает, а не отменяет. - person User; 29.07.2015

Можешь так сделать?

class Card {
    func match(othercards : [Card]) ->  Int {
        return 2 // I changed the return value just to test it
    }
}

class PlayingCard : Card {
    func match(othercards : [PlayingCard]) ->  Int {
        return 1
    }
}

Да!

В частности, вы можете, если Карта не расширяет NSObject.

class Card {
    func match(othercards : [Card]) ->  Int {
        return 2 // I changed the return value just to test it
    }
}

С другой стороны, если Card расширяет NSObject.

class Card : NSObject {
    func match(othercards : [Card]) ->  Int {
        return 2 // I changed the return value just to test it
    }
}

Тогда вы получите ошибку!

Похоже, overloading работает только с "чистыми" классами Swift.

person Luca Angeletti    schedule 25.07.2015
comment
Вы абсолютно правы . он отлично работает, если две функции объявлены в одном классе, но выдает ошибку, когда две функции объявлены в разных классах, которые наследуются от одного - person sriram hegde; 25.07.2015
comment
Пожалуйста, проголосуйте за мой вопрос, если вы сочтете это полезным. Спасибо :) - person sriram hegde; 25.07.2015
comment
Я вижу, вы меняете принятый ответ. Обратите внимание, что вы можете принять только один ответ. Таким образом, каждый раз, когда вы принимаете еще один, предыдущий теряет принятую оценку. - person Luca Angeletti; 25.07.2015
comment
И ... Кто-то задается вопросом, почему они просто потеряли очки - в первый раз, когда это случилось со мной, я подумал, что меня несколько раз проголосовали против! - person rholmes; 25.07.2015
comment
Вы знаете, почему возникает ошибка, если Card расширяет NSObject? - person bhspencer; 25.07.2015
comment
@sriramhegde, если вы помещаете две функции в один и тот же класс, вы перегружаете, а не переопределяете. Ваш исходный вопрос касается переопределения. - person bhspencer; 25.07.2015
comment
@bhspencer: Я тоже думал, что они учатся в одном классе. В первой версии своего ответа я поместил оба метода в один класс. Затем один из приведенных выше комментариев заставил меня понять, что исходный код, вероятно, был организован с использованием одного метода для каждого класса. Поэтому я соответствующим образом обновил код. Но это всего лишь предположение :) - person Luca Angeletti; 25.07.2015
comment
@bhspencer: Возможно, это ошибка, когда Card extends NSObject, потому что Objective-C не поддерживает перегрузку метода. - person Luca Angeletti; 26.07.2015
comment
Сообщение об ошибке не связано с перегрузкой метода. Речь идет о переопределении. - person bhspencer; 26.07.2015

Компилятор Swift должен видеть и PlayingCard, и Card как один и тот же тип. Если кто-то наследует (либо по подтипу, либо по протоколу (при определенных обстоятельствах)), это может объяснить это.

Хотя это можно рассматривать как запах кода, вы также можете проверить тип во время выполнения и использовать только одна функция, как в принятом здесь ответе: Проверка если объект заданного типа в Swift.

Некоторые шаблоны, которые можно применить, приведены в этом объяснении аналогичного примера - хотя вопрос касается C #, могут применяться аналогичные объектно-ориентированные методы: метод переопределения с производным параметром вместо базового.

person rholmes    schedule 25.07.2015