Можно ли предоставить реализацию метода по умолчанию в протоколе Swift?

Ответ

Да, начиная с Swift 2, можно предоставить реализацию по умолчанию для методов и вычисляемых свойств протокола с помощью расширения протокола (protocol extension).

protocol Vehicle {
    var fuelLevel: Double { get }
    func startEngine()
}

// Расширение протокола с реализацией по умолчанию
extension Vehicle {
    // Реализация метода по умолчанию
    func startEngine() {
        print("Engine started for a generic vehicle.")
    }

    // Вычисляемое свойство по умолчанию
    var isFuelLow: Bool {
        return fuelLevel < 0.1
    }
}

struct Car: Vehicle {
    var fuelLevel: Double
    // Метод startEngine() не реализован — используется версия по умолчанию
}

let myCar = Car(fuelLevel: 0.5)
myCar.startEngine() // Выводит: "Engine started for a generic vehicle."
print(myCar.isFuelLow) // Выводит: false

Важные детали:

  1. Переопределение: Тип, соответствующий протоколу, может предоставить свою собственную реализацию, которая будет иметь приоритет над реализацией по умолчанию.
  2. Ограничения: Реализация по умолчанию не может добавлять stored properties или новые требования к протоколу.
  3. Специализация: Можно создавать расширения с ограничениями where для предоставления реализаций по умолчанию только определенным типам.
    extension Vehicle where Self: Electric {
    func startEngine() { print("Silent electric engine started.") }
    }

Ответ 18+ 🔞

О, смотри-ка, какая интересная хуйня! Swift, блядь, с версии 2.0 подсуетился и сделал так, что протоколам можно давать дефолтные пиздюлины — то есть, реализацию по умолчанию. Делается это через расширение протокола, ёпта!

protocol Vehicle {
    var fuelLevel: Double { get }
    func startEngine()
}

// Вот сюда, в это расширение, и пихаем всю дефолтную логику, чтоб она не мозолила глаза
extension Vehicle {
    // Метод по умолчанию
    func startEngine() {
        print("Engine started for a generic vehicle.")
    }

    // Вычисляемое свойство по умолчанию — да, и такое можно, блядь!
    var isFuelLow: Bool {
        return fuelLevel < 0.1
    }
}

struct Car: Vehicle {
    var fuelLevel: Double
    // А тут startEngine() даже не паримся реализовывать — берём то, что дали сверху!
}

let myCar = Car(fuelLevel: 0.5)
myCar.startEngine() // Напечатает эту общую хуйню: "Engine started for a generic vehicle."
print(myCar.isFuelLow) // false, потому что бензина ещё дохуя

Но есть, конечно, свои подводные ебучки, блядь:

  1. Переопределить можно всегда. Если твоя структура или класс вдруг возьмут и скажут «нахуй эту дефолтную логику, я сам знаю, как заводиться!» — то их реализация будет главнее. Просто объявляешь свой метод внутри типа, и всё, дефолтный идёт лесом.

  2. Нельзя воткнуть stored properties. Расширение протокола — это не волшебная палочка, блядь. Требования протокола добавить или stored properties налепить через него — низя. Только методы и вычисляемые свойства.

  3. А вот это вообще мощь: можно делать специализированные дефолтные реализации через where. Типа, «вот этой конкретной категории уёбков — особый привет».

extension Vehicle where Self: Electric {
    func startEngine() { print("Silent electric engine started.") }
}

Вот так вот, блядь. Удобная штука, если не перепихивать туда всю бизнес-логику, а то получится пиздопроебибна, в которой потом сам чёрт ногу сломит.