Перейти к содержанию

Совместимость типов на основе вариантности

Помимо того, что совместимость типов зависит от вида типизации, которая была подробно разобрана в главе “Совместимость типов на основе вида типизации”, она также может зависеть от такого механизма, как вариантность.

Вариантность — это механизм переноса иерархии наследования типов на производные от них типы. В данном случае производные не означает связанные отношением наследования. Производные, скорее, означает определяемые теми типами, с которых переносится наследование.

Если вы впервые сталкиваетесь с этим понятием и определение вариантности кажется бессмысленным набором слов, то не стоит расстраиваться, эта тема очень простая, в чем вы сами скоро убедитесь.

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

Но прежде чем познакомится с каждым из этих видов вариантности отдельно, стоит сделать некоторые уточнения касательно иерархии наследования.

Иерархия наследования

Иерархия наследования — это дерево, вверху которого расположен корень, самый базовый тип (менее конкретный тип), ниже которого располагаются его подтипы (более конкретные типы). В случаях преобразования подтипа к базовому типу говорят, что выполняется восходящее преобразование (upcasting). И наоборот, когда выполняется приведение базового типа к его подтипу, говорят, что выполняется нисходящее приведение (downcasting). Отношения между супертипом и его подтипом описываются как отношение родитель-ребенок (parent-child). Отношения между родителем типа и его ребенком описываются как предок-потомок (ancestor-descendant). Кроме того, при логическом сравнении тип, находящийся выше по дереву, больше (>) чем тип находящийся ниже по дереву (и наоборот). Можно сказать, что parent > child, child < parent, ancestor > descendant, descendant < ancestor. Все это представлено на диаграмме ниже.

Этого вполне достаточно для того, чтобы приступить к разбору видов вариантности.

Ковариантность

Ковариантность — это механизм, позволяющий использовать более конкретный тип там, где изначально предполагалось использовать менее конкретный тип. Простыми словами, совместимыми считаются типы, имеющие отношение A > B и A = B.

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

Ковариантность рекомендуется применять в местах, допускающих чтение.

Контравариантность

Контвариантность — это противоположный ковариантности механизм, позволяющий использовать менее конкретный тип там, где изначально предполагалось использовать более конкретный тип. Другими словами, совместимыми считаются типы имеющие отношения A < B и A = B.

Контравариантность не рекомендуется в местах, допускающих чтение, и наоборот, рекомендуется применять в местах, допускающих запись.

Инвариантность

Инвариантность — это механизм, позволяющий использовать только заданный тип. Совместимыми считаются только идентичные типы A = A.

Бивариантность

Бивариантность — это механизм, который является представлением всех, перечисленных ранее, видов вариантности. В его случае совместимыми считаются любые из перечисленных ранее варианты типы A > B, A < B и A = B.

Бивариантность является самым нетипобезопасным видом вариантности.