https://www.raywenderlich.com/148448/introduction-protocol-oriated-programming
protocol Bird {
var name: String { get }
var canFly: Bool { get }
func doSomething()
}
protocol Flyable {
var airspeedVelocity: Double { get }
}
extension Bird {
// Flyable birds can fly!
var canFly: Bool { return self is Flyable }
func doSomething() {
print("default Bird: \(name)")
}
}
class FlappyBird: Bird, Flyable {
let name: String
let canFly = true
var airspeedVelocity: Double = 5.0
init(name: String) {
self.name = name
}
}
class Penguin: Bird {
let name: String
let canFly = false
init(name: String) {
self.name = name
}
}
class Owl<T> : Bird {
let name: String
let power: T
init(name: String, power: T) {
self.name = name
self.power = power
}
}
extension Bird where Self: FlappyBird {
func doSomething() {
print("FlappyBird: \(name)")
}
}
extension Bird where Self: Owl<String> {
func doSomething() {
print("Owl<String>: \(name)")
}
}
let fb = FlappyBird(name:"PAK")
let penguin = Penguin(name:"Mr. Pickle")
let nightOwl = Owl<String>(name:"Night Owl", power:"Who")
let dayOwl = Owl<Int>(name:"Day Owl", power: 50)
let birds: [Bird] = [fb, penguin, nightOwl, dayOwl]
birdloop: for bird in birds {
bird.doSomething()
}
Результат, который я получаю:
FlappyBird: PAK
default Bird: Mr. Pickle
default Bird: Night Owl
default Bird: Day Owl
Первый результат работает, как ожидалось, так как
extension Bird where Self: FlappyBird {
func doSomething() {
print("FlappyBird: \(name)")
}
}
Второй результат работает, как ожидалось, поскольку вызывает расширение протокола по умолчанию:
extension Bird {
// Flyable birds can fly!
var canFly: Bool { return self is Flyable }
func doSomething() {
print("default Bird: \(name)")
}
}
Третий результат, который я ожидал бы напечатать
Owl<String>: Night Owl
поскольку nightOwl относится к типу Owl<String>. Но вместо этого он вызывает расширение протокола по умолчанию:
default Bird: Night Owl
Есть какая-то причина, почему
extension Bird where Self: FlappyBird {
func doSomething() {
print("default Bird: \(name)")
}
}
называется для типа FlappyBird, но
extension Bird where Self: Owl<String> {
func doSomething() {
print("Owl<String>: \(name)")
}
}
нет вызывается для типа Owl<String>?





Для универсального типа Owl<T> вам разрешено иметь ограничение where Self: Owl<String>, но оно будет работать только в контекстах, где доступна информация о конкретном типе.
Чтобы было понятно, что происходит, примите во внимание следующее:
let nightOwl = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "Owl<String>: Night Owl"
В отличие от этого:
let nightOwl: Bird = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "default Bird: Night Owl"
Когда Swift создает таблицы свидетелей протокола для типов Owl<T> и FlappyBird, он должен действовать по-разному для каждого из них, поскольку Owl является универсальным. Если у него нет конкретной информации о типе, то есть Owl<String> на сайте вызова, он должен использовать реализацию по умолчанию для Owl<T>. Эта «потеря» информации о типе происходит, когда вы вставляете сов в массив типа [Bird].
В случае FlappyBird, поскольку существует только одна возможная реализация (поскольку она не является универсальной), компилятор создает таблицу свидетелей с «ожидаемой» ссылкой на метод, которой является print("FlappyBird: \(name)"). Поскольку FlappyBird не является универсальным, его таблица-свидетель не нуждается в какой-либо ссылке на неограниченную реализацию doSomething() по умолчанию и, следовательно, может правильно вызывать ограниченную реализацию, даже если конкретная информация о типе отсутствует.
Чтобы прояснить, что компилятору «требуется» откат для универсального типа, вы можете удалить соответствие Bird из Owl<T> и попытаться полагаться исключительно на ограниченную реализацию по умолчанию. Это приведет к ошибке компиляции с ошибкой, которая, как обычно в Swift, сильно вводит в заблуждение.
Value of type 'Owl' has no member 'doSomething'
По сути, кажется, что таблица свидетелей не может быть построена, потому что для этого требуется реализация, которая будет работать для всех типов T на Owl.
Рекомендации
@AllenHumphreys ответьте здесь - достойное объяснение того, почему.
Что касается части исправления, реализуйте doSomething() для универсального Owl как:
class Owl<T> : Bird {
//...
func doSomething() {
print("Owl<\(type(of: power))>: \(name)")
}
}
и теперь вам больше не нужен doSomething() в extension Bird where Self: Owl<String>