Тип переменной, которой вы назначаете объект, не контролирует, как функции отправляются на этот объект. При поиске функции компьютер начинает с объекта и «поднимается» вверх по иерархии классов в поисках реализации. ie. Она начинается с A
и, если она не находит функцию, она поднимается до Base
(суперкласс A
) и ищет там. Если все еще не найден, он переходит к следующему суперклассу (которого в данном случае нет). Если функция не найдена, вы получите сообщение об ошибке.
В первом примере у вас есть экземпляр A
, а A
переопределяет базовую greetings
, так что реализация всегда будет используемый. Это связано с важной концепцией в объектно-ориентированном программировании - Заменяемость .
В этом случае вы можете объявить переменную типа Base
, но назначить экземпляр более специализированного класса - A
, но программа по-прежнему работает правильно.
Помните, что вывод типа, как показано в вашем первом назначении (let a = A()
), присутствует не на всех языках. Во многих случаях вам необходимо явно объявить тип переменной. Если объявление переменной типа Base
означает, что вместо подкласса была вызвана функция Base
, это исключило бы назначение подклассов.
Что касается вашего вопроса
как происходит соответствие класса протоколу?
Протокол - это спецификация, но не реализация (далее мы рассмотрим ваш вопрос относительно реализаций по умолчанию). Ваш протокол говорит, что все вещи, которые соответствуют P
, должны обеспечивать функцию greeting
. Очевидно, что и Base
, и A
обеспечивают функцию приветствия. Ваше расширение просто добавляет условие, что A
соответствует P
. Не нужно добавлять реализацию P
, потому что метод greeting
уже существует.
Если вы удалите extension A: P
, тогда let p:P = A()
выдаст ошибку, потому что компилятор больше не знает, что A
соответствует P
.
Это подводит нас к последним двум вопросам:
Почему он всегда выбирает функцию базового класса? И Почему нет ошибки времени компиляции из-за неоднозначного вызова функции?
С Язык программирования Swift
Вы можете использовать расширения протокола, чтобы обеспечить реализацию по умолчанию к любому методу или требованию вычисляемого свойства этого протокола. Если соответствующий тип обеспечивает собственную реализацию требуемого метода или свойства, эта реализация будет использоваться вместо той, которая предусмотрена расширением.
Во втором примере вашего блока A
не переопределите Base
реализацию greeting
, поэтому реализация Base
всегда вызывается.
Реализация по умолчанию greeting
, которую вы добавили к P
, будет использоваться только в том случае, если класс, соответствующий P
, не обеспечивает реализацию.
Реализации по умолчанию могут упростить согласование с протоколом, предоставляя реализацию, где есть какое-то поведение, которое будет применяться ко всем или большинству случаев. Реализации по умолчанию всегда игнорируются, если соответствующий класс обеспечивает реализацию
Рассмотрим следующий пример:
Protocol Vehicle {
func soundHorn()
var numberOfDoors: Int
}
extension Vehicle {
func soundHorn() {
print("Beep")
}
}
class SportsCar: Vehicle {
var numberOfDoors: Int {
return 2
}
}
class Sedan: Vehicle {
var numberOfDoors: Int {
return 4
}
}
class Truck: Vehicle {
var numberOfDoors: Int {
return 2
}
func soundHorn() {
print("**HONK**")
}
}
Здесь мы определили простой протокол Vehicle
, который говорит, что Vehicle
имеет несколько дверей и может звучать как рог. Мы предоставили soundHorn
по умолчанию, который печатает "Beep". Затем мы объявили классы, которые реализуют этот протокол. Два разных типа автомобилей имеют разное количество дверей, но мы довольны звуковым сигналом по умолчанию. Для Truck
у нас есть больший, более громкий гудок, поэтому мы реализуем soundHorn
вместо использования по умолчанию:
var v: Vehicle = SportsCar()
v.soundHorn() // Prints "beep"
v = Truck()
v.soundHorn() // Prints "**HONK**"
Обратите внимание, что v
имеет тип Vehicle
, но мы получаем правильный рог на основе объекта, который фактически назначен на v
. Компилятор счастлив, потому что он знает, что все Vehicles
могут soundHorn
; ему не нужно знать конкретный тип транспортного средства c.