Что будет выведено при вызове метода из экстеншена класса и протокола в Swift
Что проверяют
Знаете ли вы разницу между динамической диспетчеризацией через таблицу свидетельств протокола и статической диспетчеризацией методов из расширения. Ответ зависит не от объекта, а от того, объявлен ли метод в теле самого протокола.
Как отвечать
Ключевое правило: если требование объявлено в протоколе, вызов уходит в реализацию конкретного типа, откуда бы вы ни звали. Если метод существует только в расширении протокола, реализация выбирается по типу переменной на этапе компиляции.
protocol Greeter {
func hello()
}
extension Greeter {
func hello() { print("protocol hello") }
func bye() { print("protocol bye") }
}
struct Impl: Greeter {
func hello() { print("impl hello") }
func bye() { print("impl bye") }
}
let a = Impl()
let b: Greeter = Impl()
a.hello() // impl hello
b.hello() // impl hello
a.bye() // impl bye
b.bye() // protocol byeСтрока b.bye() и есть суть вопроса: метода bye нет среди требований протокола, поэтому компилятор подставляет реализацию из расширения по статическому типу Greeter.
Для расширения класса правило другое: методы из расширения не попадают в таблицу виртуальных функций, диспетчеризация статическая, а объявить в расширении метод с той же сигнатурой, что уже есть в классе, компилятор просто не даст.
Где спотыкаются
Говорят, что расширение переопределяет метод класса. Такой код не соберётся: два объявления одной сигнатуры внутри одного типа это ошибка повторного объявления, а override в расширении для собственных методов класса запрещён.
Второе: не различают требование протокола и метод, живущий только в расширении, хотя весь вопрос построен именно на этом. Проверяется мгновенно: перенесите объявление в тело протокола, и поведение станет динамическим.
Третье: забывают, что статическая диспетчеризация в расширениях классов мешает наследникам. Метод из расширения базового класса наследник подменить не может, и вызов через ссылку на базовый тип уйдёт в базовую реализацию, что выглядит как сломанный полиморфизм.