Множественное наследование и интерфейсы
Суть
Множественное наследование — когда класс наследует реализацию от нескольких родителей (C++, Python). Главная проблема — diamond problem: D наследует B и C, оба наследуют A и переопределяют один метод; какой вариант получит D, и сколько копий состояния A у него будет? Из-за этой неоднозначности и хрупкости большинство языков множественное наследование классов запретили.
Как решают языки
- Java и C#: одиночное наследование классов + несколько интерфейсов; с Java 8 / C# 8 интерфейсы могут содержать default-методы, конфликт нужно разрешать явно.
- Swift и Objective-C: одиночное наследование классов + любое число протоколов. В Swift протоколы могут нести поведение через
extension, поэтому получается «множественное наследование поведения» без наследования состояния. - Миксины/трейты (Ruby, Scala, Rust) — именованные наборы методов, которые «подмешиваются» в тип; protocol extensions в Swift играют ту же роль.
Diamond в Swift и как он разрешается
protocol Flying { func move() -> String }
protocol Swimming { func move() -> String }
extension Flying { func move() -> String { "fly" } }
extension Swimming { func move() -> String { "swim" } }
struct Duck: Flying, Swimming {
// Without this the call `Duck().move()` is ambiguous: two defaults.
func move() -> String { "fly or swim" }
}
print(Duck().move()) // fly or swim
print((Duck() as any Flying).move()) // fly or swim: requirement overridden
Если Duck не реализует move() сам, компилятор выдаст ошибку неоднозначности — конфликт решается явно, а не правилами порядка, как в C++ или Python (MRO).
Почему протоколы безопаснее
- Протокол не хранит состояние (только требования к свойствам), поэтому нет двойных копий полей.
- Конфликт default-реализаций — ошибка компиляции, а не тихий выбор одного из вариантов.
- Протоколы принимаются
struct/enum/actor, которым наследование недоступно в принципе.
На что смотрит интервьюер
- Ловушка диспетчеризации: метод, объявленный только в
extension(не требование протокола), вызывается статически по типу переменной. - Понимание, что «хранимые свойства в extension запрещены» — следствие: протокол не может принести состояние, только контракт и поведение.