#swift #subclassing #protocol-oriented
#swift #создание подклассов #ориентированный на протокол
Вопрос:
У меня есть протокол, которому я присвоил некоторые значения по умолчанию:
protocol HigherProtocol {
var level: Int { get }
func doSomething()
}
extension HigherProtocol {
var level: Int { 10 }
func doSomething() {
print("Higher level is (level)")
}
}
Тогда у меня есть другой протокол, который соответствует протоколу более высокого уровня, но имеет другие значения по умолчанию и реализацию функций:
protocol LowerProtocol: HigherProtocol {}
extension LowerProtocol {
var level: Int { 1 }
func doSomething() {
print("Lower level is (level)")
}
}
Затем я создаю класс, соответствующий более высокому протоколу, а затем подкласс, соответствующий протоколу более низкого уровня:
class HigherClass: HigherProtocol {}
class LowerClass: HigherClass, LowerProtocol {}
Однако, когда я создаю экземпляр этого более низкого класса, он показывает какое-то странное поведение:
let lowerClass = LowerClass()
lowerClass.level // is 1
lowerClass.doSomething() // Prints "Lower level is 10" to the console.
Свойство по умолчанию правильное, но реализация функции по умолчанию, похоже, представляет собой гибрид двух.
Мне интересно, что здесь происходит?
Ответ №1:
Похоже, вы пытаетесь использовать протоколы для создания множественного наследования. Они не предназначены для этого, и даже если вы получите эту работу, вы будете укушены несколько раз. Протоколы не являются заменой наследования, множественного или иного. (Как правило, Swift предпочитает композицию, а не наследование в любой форме.)
Проблема здесь в том, что HigherClass соответствует HigherProtocol и поэтому теперь имеет реализации для level
и doSomething
. Нижний класс наследует от этого и хочет переопределить эти реализации. Но переопределения находятся в расширении протокола, что является неопределенным поведением. Смотрите Расширения из языка программирования Swift:
Расширения могут добавлять новые функциональные возможности к типу, но они не могут переопределять существующие функциональные возможности.
Неопределенное поведение не означает, что «оно не переопределяется». Это означает, что «может случиться все, что угодно», включая этот странный случай, когда оно иногда переопределяется, а иногда нет.
(В качестве примечания, ситуация аналогична в Objective-C. Реализация метода в двух разных категориях делает неопределенным, какой из них вызывается, и нет предупреждения или ошибки, чтобы сообщить вам, когда это произойдет. Оптимизация Swift может сделать поведение еще более удивительным.)
Я бы хотел, чтобы компилятор мог обнаруживать ошибки такого рода и выдавать ошибку, но это не так. Вам нужно будет перепроектировать свою систему, чтобы не делать этого.
Ответ №2:
Протоколы являются экзистенциальными типами, поэтому вы в замешательстве. Вам необходимо предоставить доступ к типам протоколов вашего типа класса. В вашем случае вы можете сделать LowerProtocol
HigherProtocol
так, чтобы оно печатало 10 сейчас. Давайте сделаем так
let lowerClass: LowerProtocol = LowerClass()
или
let lowerClass: HigherProtocol = LowerClass()
lowerClass.level // now prints 10
lowerClass.doSomething() // Prints "Lower level is 10" to the console.