Полиморфизм подтипов и параметрический полиморфизм
Суть
Это два способа написать код, работающий с «разными типами через один интерфейс», и в Swift у них разная механика и цена.
- Полиморфизм подтипов — значение подтипа используется там, где ожидается супертип: подкласс вместо класса, конформер вместо
any Protocol. Конкретная реализация выбирается в рантайме (vtable для классов, protocol witness table для протоколов). - Параметрический полиморфизм — дженерики: функция или тип написаны один раз для произвольного
Tс ограничениями. Тип известен на этапе компиляции, компилятор генерирует специализированную версию.
Пример
// Subtype polymorphism: a value of a subtype is used where the supertype
// is expected. Decided at runtime (vtable / witness table).
protocol Shape { func area() -> Double }
struct Circle: Shape { let r: Double; func area() -> Double { .pi * r * r } }
struct Square: Shape { let a: Double; func area() -> Double { a * a } }
let shapes: [any Shape] = [Circle(r: 1), Square(a: 2)]
let total = shapes.reduce(0) { $0 + $1.area() } // existential, dynamic
// Parametric polymorphism: code is written once for any T; the compiler
// specializes it per concrete type (static dispatch, no boxing).
func totalArea<S: Shape>(_ shapes: [S]) -> Double {
shapes.reduce(0) { $0 + $1.area() }
}
let circles = totalArea([Circle(r: 1), Circle(r: 2)]) // S == Circle
// `some Shape` = opaque type: parametric for the caller, one concrete type.
func makeShape() -> some Shape { Circle(r: 3) }
Когда что выбирать
- Гетерогенные данные (массив разных фигур, список экранов) — только подтипы:
any Shape. - Однородные данные и горячий код — дженерики или
some: нет boxing, возможны инлайнинг и специализация. - Протокол с
associatedtypeдолго нельзя было использовать какany P; с Swift 5.7 можно (any Collection<Int>), но вызовы остаются динамическими.
На что смотрит интервьюер
- Понимание, что
any P— это контейнер фиксированного размера (3 слова inline, иначе аллокация в куче) плюс указатель на witness table. some Pв возвращаемом типе: вызывающий код не знает конкретный тип, но компилятор знает — SwiftUIsome Viewименно про это.- Класс-иерархии — тоже полиморфизм подтипов, но в Swift он реже нужен: протокол даёт то же для value types.