Пределы полиморфизма


Правило Вызова Компонентов

Если тип сущности x основан на классе С , то в вызове компонента x.f сам компонент f должен быть определен в одном из предков С .

Напомним, что класс С является собственным предком. Фраза "тип сущности x основан на классе С " напоминает, что для классов, порожденных из родовых, тип может включать не только имя класса: LINKED_LIST [INTEGER] . Но базовый класс для типа - это LINKED_LIST , так что родовой параметр никак не участвует в нашем правиле.

Как и все другие правила корректности, рассматриваемые в этой книге, правило Вызова Компонентов является статическим, - его можно проверять на основе текста системы, а не по ходу ее выполнения. Компилятор (который, как правило, выполняет такую проверку) будет отвергать классы, содержащие некорректные вызовы компонентов. Если успешно реализовать проверку правил типизации, то не возникнет риск того, что скомпилированная система когда-либо во время выполнения применит некоторый компонент к объекту неподходящего типа.

Статическая типизация - это один из главных ресурсов ОО-технологии для достижения объявленной в 1-ой лекции цели - надежности ПО.

Уже отмечалось, что не все подходы к построению ОО-ПО имеют статическую типизацию. Наиболее известным представителем языков с динамической типизацией является Smalltalk, в котором не действует статическое правило вызова, но допускается, чтобы вычисление аварийно завершалось в случае возникновения ошибки: "сообщение не понятно". В лекции, посвященной типизации, будет приведено сравнение разных подходов.

 

Неограниченный полиморфизм был бы несовместим со статическим понятием типа. Допустимость полиморфных операций определяется наследственностью.

Все примеры полиморфных присваиваний, такие, как p := r и p := t , в качестве типа источника используют потомков класса-цели. Скажем, что в таком случае тип источника согласован с классом цели. Например, SQUARE согласован с RECTANGLE и с POLYGON , но не с TRIANGLE . Чтобы уточнить это понятие, дадим формальное определение:

Определение: согласованность

Тип U согласован с типом T , только если базовый класс для U является потомком базового класса для T ; при этом для универсально порожденных типов каждый фактический параметр U должен (по рекурсии) быть согласован с соответствующим формальным параметром T .

Почему недостаточно понятия потомка в этом определении? Причина снова в том, что допускается порождение из родовых классов, поэтому приходится различать типы и классы. Для каждого типа имеется базовый класс , который при отсутствии порождения совпадает с самим типом (например, POLYGON является базовым для себя). При этом для универсально порожденного класса базовым является универсальный класс с опущенными родовыми параметрами. Например, для класса LIST [POLYGON] базовым будет класс LIST . Вторая часть определения говорит о том, что B [Y] будет согласован с A [X] , если B является потомком A , а Y - потомком X .

Заметим, что поскольку каждый класс является собственным потомком, то каждый тип согласован сам с собой.

При таком обобщении понятия потомка получаем второе важное правило типизации: