Видеокурсы по изучению языка программирования Swift. Подробнее

Композиция протоколов

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

Иногда бывает удобно требовать тип соответствовать нескольким протоколам за раз. Вы можете скомбинировать несколько протоколов в одно единственное требование при помощи композиции протоколов. Композиции протоколов имеют форму SomeProtocol & AnotherProtocol. Вы можете перечислить столько протоколов, разделяя их между собой знаком амперсанда (&).

Ниже приведен пример, который комбинирует два протокола Named и Aged в одно единственное требование композиции протоколов в качестве параметра функции:

protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
    print("С Днем Рождения, \(celebrator.name)! Тебе уже \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Сашка", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Выведет "С Днем Рождения, Сашка! Тебе уже 21!"

В этом примере мы определяем протокол Named с единственным требованием свойства name типа String, значение которого мы можем получить. Так же мы определяем протокол Aged с единственным требованием свойства age типа Int, значение которого мы так же должны мочь получить. Оба этих протокола принимаются структурой Person.

Этот пример определяет функцию wishHappyBirthday(to:), которая принимает единственный параметр celebrator. Тип этого параметра Named & Aged, что означает “любой тип, который соответствует сразу двум протоколам Aged и Named”. Это не важно какой тип передается в качестве параметра функции до тех пор, пока он соответствует этим протоколам.

Далее в примере мы создает экземпляр birthdayPerson класса Person и передаем этот новый экземпляр в функцию wishHappyBirthday(to:). Из-за того что Person соответствует двум протоколам, то функция wishHappyBirthday(to:) может вывести поздравление с днем рождения.

Следующий пример показывает как вы можете объединить протокол Named с классом Location:

class Location {
    var latitude: Double
    var longitude: Double
    init(latitude: Double, longitude: Double) {
        self.latitude = latitude
        self.longitude = longitude
    }
}
class City: Location, Named {
    var name: String
    init(name: String, latitude: Double, longitude: Double) {
        self.name = name
        super.init(latitude: latitude, longitude: longitude)
    }
}
func beginConcert(in location: Location & Named) {
    print("Hello, \(location.name)!")
}
 
let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3)
beginConcert(in: seattle)
// Выведет "Hello, Seattle!"

Функция beginConcert(in:) принимает параметр типа Location и Name, что означает любой тип, который является подклассом Location, и который будет реализовывать протокол Named. В этом случае City удовлетворяем обоим требованиям.

Передавая birthdayPerson в функцию beginConcert(in:) некорректно, так как Person не является подклассом Location. И наоборот, если вы создали подкласс Location, который не реализует протокол Named, то вызов метода beginConcert(in:) с этим экземпляром так же является некорректным.

Swift: 
4.0